JVM优化——Java对象内存分配机制(04)

Java内存分配和回收的机制概括的说就是:分代分配,分代回收。对象根据其存活时间大体分为:新生代、老年代和持(永)久代

新生代(Young Generation)

对象被创建时,内存的分配首先发生在新生代,除非这个对象太大,或者超过了设置的阈值-XX:PretenureSizeThresold设置的值,则直接被分配到Old区。大部分的对象在创建后很快就不再使用,因此很快变得不可达,于是被年轻代的GC机制清理掉,这个GC机制被称为Minor GC或叫Young GC。注意,Minor GC并不代表新生代内存不足,它事实上只表示在Eden区上的GC。

老年代(Old Generation)

对象如果在年轻代存活了足够长的时间而没有被清理掉(即在几次Young GC后存活了下来),则会被复制到年老代,年老代的空间一般比年轻代大,能存放更多的对象,在年老代上发生的GC次数也比年轻代少。当年老代内存不足时,将执行Major GC,也叫 Full GC。  

Java对象分配以及回收过程
  1. 首先通过new或者newInstance等方式创建的对象,都存放在Eden区,其中大多数对象很快就会消亡,Eden区域是连续的内存区域,因此在其上分配内存极快。

  2. 在不断创建对象的过程中,Eden区域会满,这个时候会发生Young GC,第一次GC就是找出Eden中幸存活着的对象,然后将这些对象复制到S0或者S1中的一个,假设第一次选择了S0,在复制的过程中S0区域也满了,则剩下的活着的对象直接放到Old区域,然后将S0区域清空,而此时S1区域也是空的,两个Survivior区域总有一个是空的。

  3. 等下次Eden区域满的时候,再次执行Young GC,将消亡的对象清理掉,将幸存的对象复制S1区域中,如果S1中存不下则直接放到Old区域,清空Eden,然后再将S0中消亡的对象清理掉,将可以晋级的对象直接晋级到Old区域,将幸存的对象复制到S1,最后清空S0。

  4. 第三次第四次以此类推,始终保证S0和S1有一个是空的,用来存储临时对象,用于交换空间的目的,反反复复依然没有被回收的对象,则将放入Old区域中,默认回收次数是15,也可以通过-XX:MaxTenuringThreshold设置。

从上面的过程可以看出,Eden区是连续的空间,且Survivor总有一个为空。经过一次GC和复制,一个Survivor中保存着当前还活着的对象,而Eden区和另一个Survivor区的内容都不再需要了,可以直接清空,到下一次GC时,两个Survivor的角色再互换。因此,这种方式分配内存和清理内存的效率都极高,这种垃圾回收的方式就是著名的“停止-复制(Stop-and-copy)”清理法(将Eden区和一个Survivor中仍然存活的对象拷贝到另一个Survivor中),这种停止复制清理法在新生代很高效,如果在老年代采用停止复制,则效率并不高。

在Eden区,HotSpot虚拟机使用了两种技术来加快内存分配。分别是bump-the-pointer和TLAB(Thread-Local Allocation Buffers),这两种技术的做法分别是:
由于Eden区是连续的,因此bump-the-pointer技术的核心就是跟踪最后创建的一个对象,在对象创建时,只需要检查最后一个对象后面是否有足够的内存即可,从而大大加快内存分配速度。
而对于TLAB技术是对于多线程而言的,将Eden区分为若干段,每个线程使用独立的一段,避免相互影响。TLAB结合bump-the-pointer技术,将保证每个线程都使用Eden区的一段,并快速的分配内存。

问题一:如何定义对象可达?
从根引用开始,对象的内部属性可能也是引用,只要能级联到的都被认为是可达对象。

问题二:什么是根?
本地变量引用、操作数栈引用、PC寄存器、本地方法栈引用等这些都是根。

问题三:对象进入Old区域有什么坏处?
Old区域一般被称为老年代,老年代和新生代不一样,新生代我们可以认为存活下来的对象很少,而老年代则相反,存活下来的对象很多,因为这里活着的对象很多,所以发生一次Full GC找出所有存活的对象是非常耗时的,因此我们应尽量避免Full GC的发生。

问题四:S0和S1一般多大,怎么来设置?
一般来说,S0和S1的区域很小,设置参数主要有两个:

1
2
-XX:SurvivorRatio=8
-XX:InitialSurvivorRatio=8

第一个参数是Eden和Survivior区域的比值,如果设置为8,则表示Eden是S0或者S1的8倍,S0和S1一般大小一样。
第二个参数是Young/Survivor的比值,当其设置为8时,表示S0或者S1占整个Young空间的12.5%

问题五:发生Full GC即Major GC会带来什么危害?
在发生FULL GC的时候,意味着JVM会安全的暂停所有正在执行的线程(Stop The World),来回收内存空间,在这个时间段内,所有除了回收垃圾的线程外,其他有关JAVA的程序,代码都会静止,反映到系统上,就会出现系统响应大幅度变慢,卡机等状态。所以我们在编码的时候一定注意尽量少造点垃圾对象,这样出发Full GC的几率就会变小。

坚持原创技术分享,您的支持将鼓励我的继续创作