年轻代:
一般情况下,所有新生成的对象首先都是放在年轻代的。年轻代的目的就是尽可能快速的收集掉那些生命周期短的对象。年轻代分三个区。一个Eden区,两个 Survivor区(分别叫from和to)Eden区与一个Survivor区的空间比例默认为8:1。
对象在Eden区中分配,当Eden区没有足够空间进行分配时,虚拟机将发起一次Minor GC。如果对象仍然存活,并且能被Surivivor容纳,将被移动到Survivor To区中。而在“From”区中,仍存活的对象会根据他们的年龄值来决定去向。年龄达到一定值(年龄阈值 -- 默认为15,可以通过-XX:MaxTenuringThreshold来设置)的对象会被移动到年老代中,没有达到阈值的对象会被复制到“To”区域。经过这次GC后,Eden区和From区已经被清空。这个时候,“From”和“To”会交换他们的角色,也就是新的“To”就是上次GC前的“From”,新的“From”就是上次GC前的“To”。不管怎样,都会保证名为To的Survivor区域是空的。Minor GC会一直重复这样的过程,直到“To”区被填满,“To”区被填满之后,会将所有对象移动到年老代中。
为什么要分两个Surivivor区:最大的好处就是解决了碎片化。
因为采用的是复制算法。将原有的内存空间划分成两块,每次只使用其中一块,在垃圾回收的时候,将正在使用的内存中的存活对象复制到另一块内存区域中,然后清除正使用过的内存区域,交换两个区域的角色,完成垃圾回收。
为什么要是用复制算法:因为新生代gc比较频繁、对象存活率低,用复制算法在回收时的效率会更高,也不会产生内存碎片。但复制算法的代价就是要将内存折半,为了不浪费过多的内存,就划分了两块相同大小的内存区域survivor from和survivor to。在每次gc后就会把存活对象给复制到另一个survivor上,然后清空Eden和刚使用过的survivor。
年老代:
长期存活的对象将进入年老代。因此,可以认为年老代中存放的都是一些生命周期较长的对象。
持久代:
用于存放静态文件,如今Java类、方法等。持久代对垃圾回收没有显著影响,但是有些应用可能动态生成或者调用一些class,例如Hibernate 等,在这种时候需要设置一个比较大的持久代空间来存放这些运行过程中新增的类。持久代大小通过-XX:MaxPermSize=<N>进行设置。
重点:
- 在java中,对象实例都是在堆上创建。一些类信息,常量,静态变量等存储在方法区。堆和方法去都是线程共享的。
- GC机制是由JVM提供,用阿里清理需要清除的对象。回收堆内存。
- 在从内存回收一个对象之前会调用对象的finalize()方法,但是同一个对象只会调用一次。
- System.gc()和Runtime.gc()会向JVM发送执行GC的请求,但是JVM不保证一定会执行GC。
- 如果堆内存不足以创建新对象。会跑出OutOfMemoryError。
原文地址:https://www.cnblogs.com/gouge/p/9110203.html