写的很详细,为了尊重原创没有直接拷贝过来,手打了一遍,增加了记忆和理解,有疑任喷
堆内存设置
原理:
JVM堆内存分为2快:Permanent Space 和 Heap Space。
Permanent : 即 持久代(Permanent Generation),主要存放的是Java类定义信息,与垃圾收集器要收集的Java对象关系不大。
Heap = { Old +New= {Eden,from,to } }
Old 即 老年代 (Old Generation) New 即 年轻代 (Young Generation)。
年轻代和老年代的划分对垃圾收集影响比较大。
年轻代:所有新生成的对象首先都是放在年轻代。年轻代的主要目标就是尽可能快速的收集掉那些生命周期短的对象。
年轻代一般分为3个区:一个Eden区,2个Survivor区(from 和 to)。
大部分对象在Eden区中生成。当Eden区满时,还存活的对象将被复制到Survivor区(两个中的一个),当一个Survivor区满时,此区的存活对象将被复制到另外一个Survivor区;当另一个Survivor区也满了的时候,从前一个Survivor区复制过来的并且此时还存活的对象,将可能被复制到老年代。
2个survivor区是对称的,没有先后关系,所以同一个Survivor区中可能同时存在从Eden区复制过来的对象,和从另一个Survivor区复制过来的对象。
而复制到年老区的只有另一个Survivor区过来的对象。而且,因为需要交换的原因,Survivor区至少有一个是空的。特殊情况下,根据程序的需要,Survivor区是可以配置为多个的(多于2个),这样可以增加对象在年轻代的存在时间, 减少被放到年老代的可能。
针对年轻代的垃圾回收即Young GC。
年老代
在年轻代中经历了N次(可配置)垃圾回收后仍然存活的对象,就会被复制到年老代中。因此,可以认为年老代中存放的都是一些生命周期长的对象。
针对年老代的垃圾回收即Full GC
持久代
用于存放静态类型的数据,如 Java class,Method 等。持久代对垃圾回收没有显著影响。但是有些应用可能动态生成或调用一些Class,例如 Hibernate CGLib 等,在这种时候往往需要设置一个比较大的持久代空间来存放这些运行过程中动态增加的类型。
所以,当一组对象生成时,内存申请过程如下:
JVM会试图为相关Java对象在年轻代的Eden区中初始化一块内存区域。当Eden区空间足够时候,内存申请结束。否则执行下一步。JVM试图释放在Eden区中所有不活跃的对象(Young GC)。释放后若Eden空间仍然不足以放入新对象,JVM则试图将部分Eden区中活跃对象放入Survivor区,Survivor区被用来作为Eden 区及年老代的中间交换区域。当年老代空间足够时,Survivor区中存活了一定次数的对象会被移到年老代,当年老代的空间不够时,JVM会在年老代进行完全的垃圾回收(Full GC) 。Full GC 后,若Survivor区及年老区仍然无法存放从Eden区复制过来的对象,则会导致JVM无法在Eden区为新生成的对象申请内存, 即出现“Out of Memory”。
OOM(“Out of Memory”)异常一般主要有如下2种原因:
1、年老代溢出,表现为:java.lang.OutOfMemoryError:Javaheapspace
这是最常见的情况,产生的原因可能是:设置内存参数Xmax过小或程序的内存泄露及使用不当问题
2、持久代溢出,表现为:java.lang.OutOfMemoryError:PermGenspace
解决的办法唯有将参数-XX:MaxPermSize 调大(一般256m能满足绝大数应用程序需求)。将部分Java类放到容器共享区(例如Tomcat share lib)去加载的办法也是一个思路,但前提是容器里部署了多个应用,且这些应用有大量的共享类库