JVM08_堆
Posted root_zhb
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JVM08_堆相关的知识,希望对你有一定的参考价值。
堆
1、堆的核心概述
-
一个JVM实例只存在一个堆内存,堆也是Java内存管理的核心区域
-
Java堆区在JVM启动的时候即被创建,其空间大小也是确定的。是Jvm管理最大的一块内存空间
-
堆可以在物理上不连续的内存空间中,但在逻辑上是连续的
-
所有的线程共享Java堆,在这里还可以划分线程私有的缓冲区(Thread Local Allocation Buffer,TLAB)
-
在方法结束后,堆中的对象不会马上被移除,仅仅在垃圾收集的时候才有被移除
(注意:一个进程就是一个JVM实例,一个进程中包含多个线程) -
举例:
public class SimpleHeap {
private int id;
public SimpleHeap(int id) {
this.id = id;
}
public void show() {
System.out.println("My ID is " + id);
}
public static void main(String[] args) {
SimpleHeap sl = new SimpleHeap(1);
SimpleHeap s2 = new SimpleHeap(2);
}
}
Java 7及之前堆内存逻辑上分为三部分:新生区(Young/New)+养老区(Old/Tenure)+永久代(Perm)
Java 8及之后堆内存逻辑上分为三部分:新生区(Young/New)+养老区(Old/Tenure)+元空间(Meta)
新生代又被划分为Eden区和Survivor区(from区和to区、s0和s1)
约定:新生区=新生代=年轻代、养老区=老年区=老年代、永久区=永久代
一般而言:口语上的堆是不包含方法区的(7永久代,8元空间)
2、设置堆内存大小与OOM
- -Xms(物理内存的1/64):表示堆区的起始内存,等价于-XX:InitialHeapSize
- -Xmx(物理内存的1/4):则用于表示堆区的最大内存,等价于-XX:MaxHeapSize
- 通常会将-Xms和-Xmx两个参数配置相同的值,其目的是为了能够在java垃圾回收机制清理完堆区后不需要重新分隔计算堆区的大小,从而提升性能
public class HeapDemo1 {
/**
* 1. 设置堆空间大小的参数
* -Xms 用来设置堆空间(年轻代+老年代)的初始内存大小
* -X 是JVM的运行参数
* ms 是memory start
* -Xmx 用来设置堆空间(年轻代+老年代)的最大内存大小
* 2. 默认堆空间的大小
* 初始内存大小:物理电脑内存 / 64
* 最大内存大小:物理电脑内存 / 4
* 3. 手动设置 -Xms:600m -Xmx:600m
* 开发中建议将初始堆内存大小和最大堆内存设置成相同的值。
* 4. 查看设置的参数:方式一(cmd中):jps / jstat -gc 进程id
* 方式二(XX:+PrintGCDetails)
*/
public static void main(String[] args) {
//返回Java虚拟机中的堆内存总量
long initialMemory = Runtime.getRuntime().totalMemory()/1024/1024;
//返回Java虚拟机试图使用的最大堆内存量
long maxMemory = Runtime.getRuntime().maxMemory()/1024/1024;
System.out.println("-Xms:"+initialMemory+"M");
System.out.println("-Xmx:"+maxMemory+"M");
//TimeUnit.SECONDS.sleep(1000000);
}
}
idea设置运行时参数
Run——Edit Configurations——Modify options——Add VM options
3、年轻代与老年代
- 配置新生代与老年代在堆结构占比
默认:-XX:NewRatio=2,表示新生代占1,老年代占2,新生代占整个堆的1/3
可以修改-XX:NewRatio=4,表示新生代占1,老年代占4,新生代占整个堆的1/5 - -XX:SurvivorRatio调整这个空间比例(Eden空间和另外两个Survivor空间缺省所占的比例是8:1:1)
- -Xmn:设置新生代最大内存大小,一般使用默认值就可以了
- 几乎所有的Java对象都是在Eden区被new出来的,绝大部分的Java对象的销毁都在新生代进行的
4、图解对象分配过程
总结:
1. 针对幸存者s0,s1区的总结:复制之后有交换,谁空谁是to。
2. 关于垃圾回收:频繁在新生区收集,很少在养老区收集,几乎不在永久区/元空间收集。
5、Minor GC、Major GC、Full GC
HotSpot VM中GC按照回收区域分为: 部分收集(Partial GC)、整堆收集(Full GC)
1、部分收集(Partial GC):不是完整收集整个Java堆的垃圾收集。
-
新生代收集(Minor GC / Young GC):只是新生代的垃圾收集。
-
老年代收集(Major GC / Old GC ):只是老年代的垃圾收集。
目前,只有CMS GC会有单独收集老年代的行为。
注意,很多时候Major GC 会和 Full GC 混淆使用,需要具体分辨是老年代回收还是整堆回收。 -
混合收集(Mixed GC):收集整个新生代以及部分老年代的垃圾收集。
目前,只有G1 GC 会有这种行为
2、 整堆收集(Full GC):收集整个java堆和方法区的垃圾收集。
① YONG GC(minor GC) 新生代
当Eden代满,会触发minor GC ,Survivor 满不会引发GC
Java对象大多具备朝生夕死的特性,所以Minor GC非常频繁,一般回收速度也比较快
minor gc 会引发STW,暂停其他用户线程,等垃圾回收结束,用户线程才能恢复
②Major GC(Old GC) 老年代
major gc 的速度一般比Minor gc 慢10倍以上,STW时间更长
老年代空间不足时,会先尝试触发Minor GC(但并不是绝对的)。如果之后空间还不足,则触发Major GC
如果Major GC后,内存还不足,就报OOM
③. Full GC 堆+方法区
触发Full GC 执行的情况有如下五种:
- 调用System.gc()时,系统建议执行Full GC,但是不必然执行
- 老年代空间不足
- 方法区空间不足
- 通过Minor GC后进入老年代的平均大小大于老年代的可用空间
- 由Eden区、From Space向To Space复制时,对象大小大于To Space可用内存,则把对象转存到老年代,且老年代的可用内存小于该对象的大小。
说明:full gc是开发或调优中尽量避免的。这样暂停时间会短一些。
其他:full gc 的时候新生代完全被清空。
6、堆空间分代思想
为什么需要把Java堆分代?不分代就不能正常工作了吗?
-
经研究,不同对象的生命周期不同。70%-99%的对象是临时对象。
- 新生代:有Eden、两块大小相同的Survivor(又称为from/to,s0/s1)构成,to总为空。
- 老年代:存放新生代中经历多次GC仍然存活的对象。
-
其实不分代完全可以,分代的唯一理由就是优化GC性能。
(避免全局扫描、对症下药,因材 [不同的垃圾] 施教 [不同的GC])- 如果没有分代,那所有的对象都在一块,GC的时候要找到哪些对象没用,这样就会对堆的所有区域进行扫描。
- 很多对象都是朝生夕死的,如果分代的话,把新创建的对象放到某一地方,当GC的时候先把这块存储“朝生夕死”对象的区域进行回收,这样就会腾出很大的空间出来。
7、内存分配策略
针对不同年龄段的对象分配原则如下所示:
- 优先分配到Eden
- 大对象直接分配到老年代(尽量避免程序中出现过多的大对象)
- 长期存活的对象分配到老年代
- 动态对象年龄判断
(如果Survivor 区中相同年龄的所有对象大小的总和大于survivor空间的一半,年龄大于或等于该年龄的对象可以直接进入老年代,无须等到MaxTenuringThreshold中要求的年龄。) - 空间分配担保 -XX:HandlePromotionFailure
(JDK6之后,只要老年代的连续空间大于新生代对象总大小或者历次晋升的平均大小就会进行Minor GC,否则将进行Full GC。)
8、为对象分配内存:TLAB
9、小结堆空间的参数设置
10、堆是分配对象的唯一选择吗
以上是关于JVM08_堆的主要内容,如果未能解决你的问题,请参考以下文章
大数据技术之_30_JVM学习_01_JVM 位置+JVM 体系结构概览+堆体系结构概述+堆参数调优入门+JVM 的配置和优化+Tomcat 的配置和优化