Java开发有个很基础的问题,虽然咱们平时接触的很少,可是了解它却成为Java开发的必备基础——这就是JVM。在C++中咱们须要手动申请内存而后释放内存,不然就会出现对象已经再也不使用内存却仍被占用的状况。在Java中JVM内置了垃圾回收的机制,帮助开发者承担对象的建立和释放的工做,极大的减轻了开发的负担。那是否是咱们就不须要了解JVM了,显然在作一些优化或者深刻研究应用性能的时候,JVM仍是起了很关键的做用的。所以本篇就总结性的描述下JVM的内存模型与垃圾回收相关的知识。html
本文的主要内容以下:java
这几个存储区最主要的就是栈区和堆区,那么什么是栈什么是堆呢?说的简单点,栈里面存放的是基本的数据类型和引用,而堆里面则是存放各类对象实例的。算法
堆与栈分开设计是为何呢?多线程
栈的大小能够经过-XSs设置,若是不足的话,会引发java.lang.StackOverflowError的异常并发
线程私有,生命周期与线程相同。每一个方法执行的时候都会建立一个栈帧(stack frame)用于存放 局部变量表、操做栈、动态连接、方法出口。性能
存放对象实例,全部的对象的内存都在这里分配。垃圾回收主要就是做用于这里的。优化
所以通常都建议把这两个参数设置成同样大,能够避免JVM在不断调整大小。spa
这里记录了线程执行的字节码的行号,在分支、循环、跳转、异常、线程恢复等都依赖这个计数器。线程
类型信息、字段信息、方法信息、其余信息设计
总结
名称 | 特征 | 做用 | 配置 | 异常 |
---|---|---|---|---|
栈区 | 线程私有,使用一段连续的内存空间 | 存放局部变量表、操做栈、动态连接、方法出口 | -XSs | StackOverflowError OutOfMemoryError |
堆 | 线程共享,生命周期与虚拟机相同 | 保存对象实例 | -Xms -Xmx -Xmn | OutOfMemoryError |
程序计数器 | 线程私有、占用内存小 | 字节码行号 | 无 | 无 |
方法区 | 线程共享 | 存储类加载信息、常量、静态变量等 | -XX:PermSize -XX:MaxPermSize | OutOfMemoryError |
有两种方式,一种是引用计数(可是没法解决循环引用的问题);另外一种就是可达性分析。
判断对象能够回收的状况:
这种方法优势就是减小停顿时间,可是缺点是会形成内存碎片。
这种方法不涉及到对象的删除,只是把可用的对象从一个地方拷贝到另外一个地方,所以适合大量对象回收的场景,好比新生代的回收。
这种方法能够解决内存碎片问题,可是会增长停顿时间。
最后的这种方法是前面几种的合体,即目前JVM主要采起的一种方法,思想就是把JVM分红不一样的区域。每种区域使用不一样的垃圾回收方法。
上面能够看到堆分红三个区域:
这里能够详细的说一下新生代复制回收的算法流程:
在新生代中,分为三个区:Eden, from survivor, to survior。
这种收集器就是以单线程的方式收集,垃圾回收的时候其余线程也不能工做。
以多线程的方式进行收集
大体的流程为:初始标记--并发标记--从新标记--并发清除
大体的流程为:初始标记--并发标记--最终标记--筛选回收
JVM里的GC(Garbage Collection)的算法有不少种,如标记清除收集器,压缩收集器,分代收集器等等,详见HotSpot VM GC 的种类
如今比较经常使用的是分代收集(generational collection,也是SUN VM使用的,J2SE1.2以后引入),即将内存分为几个区域,将不一样生命周期的对象放在不一样区域里:young generation,tenured generation和permanet generation。绝大部分的objec被分配在young generation(生命周期短),而且大部分的object在这里die。当young generation满了以后,将引起minor collection(YGC)。在minor collection后存活的object会被移动到tenured generation(生命周期比较长)。最后,tenured generation满以后触发major collection。major collection(Full gc)会触发整个heap的回收,包括回收young generation。permanet generation区域比较稳定,主要存放classloader信息。
young generation有eden、2个survivor 区域组成。其中一个survivor区域一直是空的,是eden区域和另外一个survivor区域在下一次copy collection后活着的objecy的目的地。object在survivo区域被复制直到转移到tenured区。
咱们要尽可能减小 Full gc 的次数(tenured generation 通常比较大,收集的时间较长,频繁的Full gc会致使应用的性能收到严重的影响)。
堆内存GC
JVM(采用分代回收的策略),用较高的频率对年轻的对象(young generation)进行YGC,而对老对象(tenured generation)较少(tenured generation 满了后才进行)进行Full GC。这样就不须要每次GC都将内存中全部对象都检查一遍。
非堆内存不GC
GC不会在主程序运行期对PermGen Space进行清理,因此若是你的应用中有不少CLASS(特别是动态生成类,固然permgen space存放的内容不只限于类)的话,就极可能出现PermGen Space错误。
内存申请、对象衰老过程
1、内存申请过程
2、对象衰老过程
GC类型 | 触发条件 | 触发时发生了什么 | 注意 | 查看方式 |
YGC | eden空间不足 | 清空Eden+from survivor中全部no ref的对象占用的内存 从新调整Eden 和from的大小(parallel GC会触发此项) |
全过程暂停应用 是否为多线程处理由具体的GC决定 |
jstat –gcutil gc log |
FGC | old空间不足 |
清空heap中no ref的对象 permgen中已经被卸载的classloader中加载的class信息 如配置了CollectGenOFirst,则先触发YGC(针对serial GC) 如配置了ScavengeBeforeFullGC,则先触发YGC(针对serial GC) |
全过程暂停应用 是否为多线程处理由具体的GC决定 是否压缩须要看配置的具体GC |
jstat –gcutil gc log |
参考 https://www.cnblogs.com/xing901022/p/7725961.html
http://www.cnblogs.com/redcreen/archive/2011/05/04/2037056.html