JVM-堆内存、GC以及JVM常见设置

1.内存区域划分

Java堆是线程共享的,堆主要分为两个区域新生代(Young/New Generation )与老年代(Old/Tenured Generation )

1.1新生代

程序中新建的对象的储存区域。

1.1.1新生代区域划分

程序中新建的对象都将分配到新生代中,新生代又由Eden(伊甸园)与两块Survivor(幸存者) Space 构成。Eden 与Survivor Space 的空间大小比例默认为8:1,即当Young/New Generation 区域的空间大小总数为10M 时,Eden 的空间大小为8M,两块Survivor Space 则各分配1M,这个比例可以通过-XX:SurvivorRatio 参数来修改。Young/New Generation的大小则可以通过-Xmn参数来指定。

1.1.1.1Eden(伊甸园)

刚刚新建的对象会被分配到伊甸园当中这里是对象的乐土。

1.1.1.1.1TLAB(Thread-local allocation buffer).

TLAB是伊甸园中的一个子区域。因为java堆是线程共享的,也就是说在我们执行new的时候其实是有加锁的操作,这会导致运行效率的降低,于是就有了TLAB。这个区域位于伊甸园当中,但是每个线程都独有一个TLAB,默认大小为伊甸园的1%,大部分时间是在做一个小变量的内存分配。

1.1.1.2Survivor(幸存者)

幸存者区域是新生代与老年代的过渡带,在每次发生Minor GC的时候,JVM会将伊甸园与一个幸存者的有效变量转移到另外一个空闲的幸存者当中,这样可以高效、快速的进行内存的GC操作。

1.1.2新生代GC(Minor GC)

指发生在新生代的垃圾收集动作,因为 Java 对象大多都具备朝生夕灭的特性,通常很多的对象都活不过一次GC,所以Minor GC 非常频繁,一般回收速度也比较快。

1.2老年代

老年代用于存放程序中经过几次垃圾回收后还存活的对象,例如缓存的对象等,老年代所占用的内存大小即为-Xmx 与-Xmn 两个参数之差。JVM默认设置中当一个对象经过16次Minor GC,这个对象将转入老年代,可以通过参数 -XX:MaxTenuringThreshold 来设置。或者当伊甸园中的变量超过了新生代的10%的时候也会发生转入老年代的操作。

1.2.1老年代GC(Major GC/Full GC)

老年代GC发生的频率不高,一般只发生在当堆内存被耗尽的时候。

老年代GC首先会暂停整个JVM中运行的程序。

然后对所有节点进行遍历,将有效对象标记出来。

最后清除所有的未标记对象/将存活对象重新整理。

Major GC的耗时较长一般为Minor GC的10倍以上,同时他会暂停JVM中运行的程序,非常影响用户的体验,这也就是为什么Android手机会”越用越卡”。

2.JVM常见设置

 

配置参数 功能
-Xms 初始堆大小。如:-Xms256m
-Xmx 最大堆大小。如:-Xmx512m
-Xmn 新生代大小。通常为 Xmx 的 1/3 或 1/4。新生代 = Eden + 2 个 Survivor 空间。实际可用空间为 = Eden + 1 个 Survivor,即 90%
-Xss JDK1.5+ 每个线程堆栈大小为 1M,一般来说如果栈不是很深的话, 1M 是绝对够用了的。
-XX:NewRatio 新生代与老年代的比例,如 –XX:NewRatio=2,则新生代占整个堆空间的1/3,老年代占2/3
-XX:SurvivorRatio 新生代中 Eden 与 Survivor 的比值。默认值为 8。即 Eden 占新生代空间的 8/10,另外两个 Survivor 各占 1/10
-XX:PermSize 永久代(方法区)的初始大小
-XX:MaxPermSize 永久代(方法区)的最大值
-XX:+PrintGCDetails 打印 GC 信息
-XX:+HeapDumpOnOutOfMemoryError 让虚拟机在发生内存溢出时 Dump 出当前的内存堆转储快照,以便分析用

 

注意:PermSize永久代的概念在jdk1.8中已经不存在了,取而代之的是metaspace元空间,当认为执行永久代的初始大小以及最大值是jvm会给出如此下提示:
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option PermSize=30m; support was removed in 8.0
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=30m; support was removed in 8.0

3.逃逸分析

逃逸分析是JVM编译源码中的一步,在这一步当中JVM虚拟机会分析源码,看new的对象是应该放到堆上还是栈上。因为很多对象都是仅仅在本函数/本线程中进行调用,很多时候是不需要利用共享内存的堆的,这样的方法效率实在是不高。

4.Java对象的内存分配过程

1.进行逃逸分析,看应该是放在栈上还是堆上。

2.如果不能放在栈上,看是否能放到TLAB当中。

3.如果不能放到TLAB当中则把内存放到伊甸园当中。

4.经历过Minor GC后会转移到幸存者区域当中。

5.经历过多次Minor GC/幸存者区域内存不足时,进入老年代。

打赏