- JVM结构分区,及优化/Jvm内存模型。堆和栈的区别,jvm堆栈静态区分别存储的内容
JVM内存主要有三大块:堆内存,方法区和栈。(方法区和堆是所有线程共享的区域,而java栈,本地方法栈和程序计数器是线程私有的)。
- 堆内存:JVM启动时创建,唯一目的是存放对象实例,几乎所有的对象实例都在这里分配。因此java堆时垃圾收集器管理的主要区域(GC堆)。
从内存回收的角度看,现在垃圾收集器基本采用分代收集算法,所以java堆还可以细分为:新生代和老年代,再细致一点可以分为Eden空间,From Survivor空间,To Survivor空间等
(可通过-Xms和-Xmx控制)
- 方法区:用于存放已经被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据。运行时常量池时方法区的一部分。用于存放编译期生成的各种字面量和符号的引用,这部分类容将在类加载后进入运行时常量池中存放。方法区也被称为“永久代”。
- 程序计数器:较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支,循环,跳转,异常处理,线程回复都需要依赖这个计数器来完成。(唯一一个没有规定任何OOM情况的区域)
- JVM栈:私有,生命周期与线程相同。是描述Java方法执行的内存模型:每个方法被执行的时候都会同时创建一个栈贞,用于存储局部变量表,操作栈,动态链接,方法出口等信息。每一个方法被调用直至执行完成的过程,就对应着一个栈贞在虚拟机栈中从入栈到出栈的过程。
Java对这个区域规定了2种异常状况。即如果线程请求深度大于虚拟机栈所允许的深度,将抛出StackOverflowError 异常,若虚拟机栈可以动态扩展,当扩展时没有申请到足够的内存时,会抛出OutOfMemoryError异常。
- 本地方法栈:与虚拟机栈作用非常相似,区别是虚拟机栈为虚拟机执行java方法服务,而本地方法栈则是为虚拟机使用到的Native方法服务。
- OOM异常
OutOfMemoryError堆溢出:
OutOfMemoryError栈溢出:
OutOfMemoryError栈溢出:
方法区溢出:在1.8中方法区已经不存在了。
直接内存溢出:我们在使用NIO的时候声明了一块区域,由于这块区域太小使的整个直接内存放不下,从而OOM。
- GC
弄清楚:哪些内存需要回收?
什么时候回收?
如何回收?
- 对象回收判断:
引用计数法(java并不使用引用计数法),即如果有引用指向这个对象,则把这个对象计数加1,如果一个对象基数为0,说明这个对象没有被引用,可以回收。存在明显的问题:
可达性分析:即从一系列的ROOT出发,能够达到的对象就认为是不应该被回收的,其他对象无论是成环还是其他形状的对象都会进行回收。
- 可以作为GC Roots的对象有:
- 虚拟机栈中引用的对象
- 方法区中的静态变量能够联系到的堆中的对象。
方法区中类静态属性引用的对象()
方法区中常量引用的对象
- 强引用:垃圾回收器永远不会回收掉引用的对象。
软引用:
弱引用:
虚引用:
- 要真正宣告一个对象死亡,至少要经历两次标记过程,进行可达性分析后没有与GC Root相连的对象,会被第一次标记,然后进行筛选,帅选条件是对象是否有必要执行Finalize()方法。
3.垃圾收集算法
A.标记清除法:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。存在的问题:有可能清理之后空间中有很多不连续的可用空间,碎片化情况严重,优势是简单
B.复制算法:将堆空间分成两边,每次只使用其中一块,当这一块内存用完了,就将还存活的对象复制到另一块上面,然后再把已使用过的内存空间一次清理。清理简单,劣势在于造成空间浪费。
大都用这种算法来回收新生代,即将内存分为一块较大的Eden和两块较小的Survivor空间,每次使用Eden和其中一块Survivor。会收拾,将Eden和Survivor中还存活的对象一次性复制到另一块Survivor空间上,(8:1:1)。当Survivor空间不足时需要老年代进行飞陪担保。
- 标记-整理:在标记清除的算法上进行了优化,将标记后存活的对象放在堆空间的一侧,然后直接清理掉边界以外的内存。老年代中由于生命比较长久,使用标记整理算法或者标记清除算法。
- 分代收集算法:即一般把java堆分为新生代和老年代,可以根据每个年代的特点采用适当的收集算法。新生代每次收集都会有大批对象死亡,少量存活,适合复制算法。老年代存活效率高,没有额外空间对他进行担保,必须使用标记清理和标记整理算法来进行回收。
4.常用的垃圾收集器
A.CMS收集器:
B.G1收集器:
6. Minor GC,Major GC,Full GC
1.Minor GC:从年轻代回收内存被称为Minor GC,当JVM无法为一个新的对象分配空间的时候会触发Minor GC,比如当Eden区满了时。分配率越高,月频繁执行Minor GC。
2.Major GC:老年代进行的垃圾回收,发生一次Major GC至少伴随一次Minor GC,一般比Minor GC的速度慢10倍以上。
3.Full GC 是清理堆空间,包括年轻代和永久代。
导致FullGC的几种情况和调优策略:
- 旧生代空间不足:调优时尽量让对象在新生代GC时被回收,让对象在新生代多存活一段时间和不要创建过大的对象及数组,避免直接在老年代创建对象。
- 持久带空间不足:用参数增大持久代的空间,避免太多静态对象。
- 统计的到的GC后晋升到老年代的平均大小大于旧生代剩余空间:控制好老年代和新生代的比例。
- System.gc()手动调用:垃圾回收尽量不要手动触发,尽量依靠自身机制,即使手动触发,JVM也不一定会去执行。
6. JVM的内存分配
对象主要分配在新生区的Eden区上,如果启动了本地线程分配缓冲,将按线程优先在TLAB上分配。少数情况下也可能分配到老年代,分配的规则不是100%固定的。
几条最普通的内存分配规则:
- 对象优先在Eden上分配,当Eden上没有足够空间时,虚拟机将发起一次Minor GC
- 大对象直接进入老年代,这样做的目的是避免在Eden区和Survivor区之间发生大量拷贝(新生代采用复制算法收集内存)。
- 长期存活的对象将进入老年代。虚拟机为每个对象定义了一个年龄计数器,如果对象经过一次Minor GC那么对象会进入Survivor区,之后每经过一次Minor GC那么独享年龄加1,至到达到阀值对象进入老年去
- 动态判断对象的年龄。如果Survivor区相同年龄的所有对象大小的综合大于Survivor区的一半,年龄大于或等于该年龄的对象可以直接进入老年代。
- 空间分配担保:每次进行Minor GC时,JVM会计算Survivor区移至老年区的对象的平均大小,如果这个值大于老年区的剩余值大小则进行一次Full GC,如果小于检查HandlePromotionFailure设置,如果true则只进行Monitor GC,如果false则进行Full GC。
7.内存模型
8. JVM如何判断是字节码文件的?
9. 类加载机制
- stop the World
VM提供两种较为简单的GC策略的设置方式:
1)吞吐量优先
JVM以吞吐量为指标,自行选择相应的GC策略及控制新生代与旧生代的大小比例,来达到吞吐量指标。这个值可由-XX:GCTimeRatio=n来设置
2)暂停时间优先
JVM以暂停时间为指标,自行选择相应的GC策略及控制新生代与旧生代的大小比例,尽量保证每次GC造成的应用停止时间都在指定的数值范围内完成。这个值可由-XX:MaxGCPauseRatio=n来设置

浙公网安备 33010602011771号