JVM 调优总结

别来无恙 提交于 2019-12-28 20:20:14

一、堆与栈

1、堆是所有对象共享的,存储对象信息;

每一个线程都有一个独立的线程栈,栈存储的是与当前线程相关的信息,包括局部变量、程序运行状态、方法返回值等。


2、堆中存储的是对象,栈中存储的是基本类型堆中对象的引用


3、栈的大小配置  -Xss


4、在运行栈中,基本类型和引用的处理是一样的,都是传值。


5、可以把一个对象看作为一棵树,对象的属性如果还是对象,则还是一棵树(非叶子节点),而基本类型则是叶子节点。


二、Java对象的大小

1、一个空的Object对象:

Object obj = new Object();

其所占的空间为: 8+4 =12 bytes 。其中8 bytes 是Java堆中对象的所需空间,4 bytes是栈中保存引用的所需空间


2、以下类

class NewObject

{

    int count;

    boolean flag;

    Object obj;

}

其的对象  NewObject  newObj = new NewObject();

newObj 所占空间 8 + 4 +1 + 4 = 17 bytes.

其中8 bytes 为 newObj 空对象大小,4 bytes为 int 大小,1 byte为 boolean 大小, 4 bytes为 obj引用大小。

又因为Java对象内存分配以8的整数倍来分,故而对象的大小为 24 bytes。


3、基本类型包装类的大小至少是16bytes。


三、强引用、软引用、弱引用

强引用:回收时候严格判断

软引用:内存紧张则回收,富余则不回收

弱引用:每次GC必定回收,生命周期只存在于一个垃圾回收周期。


四、GC 基本策略


1、引用计算:无法处理循环引用


2、标记-清除:从root节点开始标记所有引用对象,遍历堆,清除未标记对象。缺点是需要暂停整个应用,产生内存碎片。


3、复制:将内存分为两块,每次只使用其中一块。回收时候复制到另一块。


4、标记整理:结合 “标记-清除”  和 “复制”, 不过不是复制到另一块内存,而是将对象放紧凑。


五、GC 分区策略


1、增量收集:实时垃圾回收。


2、分代收集:把对象分为年轻代、年老代、持久代,对于不同生命周期对象采用不同算法。


六、GC 线程策略


1、串行收集:单线程处理,无法使用多处理器的优势。需要暂停整个运行环境。

优点:简单,效率高。

缺点:只适合小型应用,数据量比较小。

配置:-XX:+UseSerialGC


2、并行收集:多线程处理,速度快,效率高。需要暂停整个运行环境。

优点:吞吐量大,适用于数值计算、后台处理。

缺点:响应时间长。

配置:XX:+UseParallelGC   -XX:+UseParallelOldGC    -XX:ParallelGCThreads=<N>   -XX:MaxGCPauseMillis=<N>   -XX:GCTimeRatio=<N>


3、并发收集:GC的同时不需要暂停整个运行环境。

优点:响应时间快,适用于Web服务器等。

缺点:吞吐量没那么大。

配置:-XX:+UseConcMarkSweepGC  -XX:CMSInitiatingOccupancyFraction=<N>


七、分代回收


1、年轻代(Yong):一个Eden区,两个Survivor区。Eden满,放入一个Survivor。Survivor放入另一个Survivor。另一个Survivor满,放入年老代。


2、年老代(Tenured):年轻代经历N次GC后,对象放到年老区。放的是生命周期较长的数据。


3、持久代(Permanent):Java 类的 Class 信息,与GC关系不大。


Minor GC:只要Eden满即触发,速度很快。

Full GC:整个堆回收,速度慢。在年老代或者持久代满的时候调用。


八、典型配置

java -Xmx3550m -Xms3550m -Xmn2g –Xss128k  -XX:NewRatio=4 -XX:SurvivorRatio=4 -XX:MaxPermSize=16m -XX:MaxTenuringThreshold=0 XX:+UseConcMarkSweepGC -XX:+UseParNewGC  

-Xmx3550m: 最大可用内存

-Xms3550m:初始内存。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。

-Xmn2g:年轻代大小

-Xss128k:每个线程的栈大小

-XX:NewRatio=4: 年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代)

-XX:SurvivorRatio=4:年轻代中Eden区与Survivor区的大小比值

-XX:MaxPermSize=16m: 持久代大小 

-XX:MaxTenuringThreshold=0: 垃圾最大年龄。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代

-XX:+UseParallelGC:选择并行收集器

-XX:ParallelGCThreads=20: 并行收集器的线程数

-XX:+UseParallelOldGC: 年老代垃圾收集方式为并行收集

-XX:MaxGCPauseMillis=100:每次年轻代垃圾回收的最长时间

-XX:+UseAdaptiveSizePolicy:自动选择年轻代区大小和相应的Survivor区比例

-XX:+UseConcMarkSweepGC:设置年老代为并发收集

-XX:+UseParNewGC: 设置年轻代为并行收集

-XX:CMSFullGCsBeforeCompaction=5:运行多少次GC以后对内存空间进行压缩、整理。

-XX:+UseCMSCompactAtFullCollection:打开对年老代的压缩


九、调优总结


1、吞吐量优先:

年轻代大并行垃圾收集、年老代小

尽可能回收掉大部分短期对象,减少中期的对象,而年老代尽存放长期存活对象。


2、响应时间优先:

年轻代大、并发垃圾回收、年老代适中。

年老代使用并发收集器,所以其大小需要小心设置,一般要考虑并发会话率和会话持续时间等一些参数。如果堆设置小了,可以会造成内存碎片、高回收频率以及应用暂停而使用传统的标记清除方式;如果堆大了,则需要较长的收集时间


十、年老代碎片问题:

解决:

1、-XX:+UseCMSCompactAtFullCollection:使用并发收集器时,开启对年老代的压缩。

2、-XX:CMSFullGCsBeforeCompaction=0:上面配置开启的情况下,这里设置多少次Full GC后,对年老代进行压缩


十一、常见JVM 异常

1、java.lang.OutOfMemoryError: Java heap space

原因:典型的内存泄露,所有堆空间都被无法回收的垃圾对象占满。

解决:找到泄露点,一般是集合对象引用。


2、java.lang.OutOfMemoryError: PermGen space

原因:持久代占满,无法为新的class分配存储空间而引发的异常。主要原因是大量动态反射生成的类不断被加载。不同的classloader 即便使用了相同的类,但是都会对其进行加载,相当于有一个class会被N个classloader加载N次。

解决:-XX:MaxPermSize=16m


3、java.lang.StackOverflowError

原因:递归没有返回,或者循环调用。

解决:修正代码


4、Fatal: Stack size too small

原因:线程空间大小被限制

解决:增大线程栈,-Xss2m。也可能是代码内存泄露。


5、java.lang.OutOfMemoryError: unable to create new native thread

原因:操作系统没有足够资源产生这个线程

解决:配置系统,如ulimit。减小线程栈大小,-Xss。

 


 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!