JVM之堆详解
作者:互联网
1.堆的概述
- Sun公司---- HotSpot
- BEA ----JRockit
- IBM ----J9 VM
堆(Heap),一个JVM只有一个堆内存,堆内存的大小是可以调节的
类加载器读取类文件后,一般会把什么东西放到堆中?类,方法,常量,变量,保存我们所有引用类型的真实对象
- JVM内存划分为堆内存和非堆内存,堆内存分为年轻代(Young Generation)、老年代(Old Generation),非堆内存就一个永久代(Permanent Generation)。
- 年轻代又分为Eden和Survivor区。Survivor区由FromSpace和ToSpace组成。Eden区占大容量,Survivor两个区占小容量,默认比例是8:1:1。
- 堆内存用途:存放的是对象,垃圾收集器就是收集这些对象,然后根据GC算法回收。
- 非堆内存用途:永久代,也称为方法区,存储程序运行时长期存活的对象,比如类的元数据、方法、常量、属性等。
GC垃圾回收,主要是在伊甸区和养老区
假设内存满了,OOM,堆内存不够 !java.lang.OutOfMemoryError.java.heap.space
在JDK8后,永久存储区改了名字(元空间)
2.新生区 、老年区、永久区
新生区(Young Generation)
新生成的对象优先存放在新生区中,新生区对象朝生夕死,存活率很低,在新生代中,常规应用进行一次垃圾收集一般可以回收70% ~ 95% 的空间,回收效率很高。
- HotSpot将新生区划分为三块,一块较大的Eden(伊甸)空间和两块较小的Survivor(幸存者)空间,默认比例为8:1:1。划分的目的是因为HotSpot采用复制算法来回收新生代,设置这个比例是为了充分利用内存空间,减少浪费。新生成的对象在Eden区分配(大对象除外,大对象直接进入老年代),当Eden区没有足够的空间进行分配时,虚拟机将发起一次Minor GC。
- GC开始时,对象只会存在于Eden区和From Survivor区,To Survivor区是空的(作为保留区域)。GC进行时,Eden区中所有存活的对象都会被复制到To Survivor区,而在From Survivor区中,仍存活的对象会根据它们的年龄值决定去向,年龄值达到年龄阀值(默认为15,新生区中的对象每熬过一轮垃圾回收,年龄值就加1,GC分代年龄存储在对象的header中)的对象会被移到老年区中,没有达到阀值的对象会被复制到To Survivor区。接着清空Eden区和From Survivor区,新生区中存活的对象都在To Survivor区。接着, From Survivor区和To Survivor区会交换它们的角色,也就是新的To Survivor区就是上次GC清空的From Survivor区,新的From Survivor区就是上次GC的To Survivor区,总之,不管怎样都会保证To Survivor区在一轮GC后是空的。GC时当To Survivor区没有足够的空间存放上一次新生代收集下来的存活对象时,需要依赖老年区进行分配担保,将这些对象存放在老年区中。
经过研究,99%的对象都是临时对象。
老年区(Old Generationn)
在新生代中经历了多次(具体看虚拟机配置的阀值)GC后仍然存活下来的对象会进入老年代中。老年代中的对象生命周期较长,存活率比较高,在老年代中进行GC的频率相对而言较低,而且回收的速度也比较慢。
永久区(Permanent Generationn)
永久区存储类信息、常量、静态变量、即时编译器编译后的代码等数据,对这一区域而言,Java虚拟机规范指出可以不进行垃圾收集,一般而言不会进行垃圾回收。
- JDK1.6之前,永久代,常量池在方法区
- JDK1.7,永久代,但是慢慢退化了,去永久代,常量池在堆中
- JDK1.8之后,无永久代,常量池在元空间
这个区域常驻内存的,用来存放JDK自身携带的Class对象,Interface元数据,存储的是Java运行的一些环境或者类信息,这个区域不存在垃圾回收。关闭VM虚拟机就会释放这个区域的 内存。
一个启动类加载了第三方jar包,tomcat部署了太多的应用,大量动态生成的反射类,不断的被加载,直到内存满,就会出现OOM.
package com.newer;
public class Demo2 {
public static void main(String[] args) {
// 返回虚拟机视图使用的最大内存
long maxMemory = Runtime.getRuntime().maxMemory();
// 返回虚拟机视图(JVM)使用的初始化总内存
long totalMemory = Runtime.getRuntime().totalMemory();
System.out.println("maxMemory="+maxMemory+"字节\t"+(maxMemory/(double)1024/1024)+"MB");
System.out.println("totalMemory="+ totalMemory+"字节\t"+( totalMemory/(double)1024/1024)+"MB");
}
}
3.堆内存调优
调节堆内存
package com.newer;
public class Demo {
public static void main(String[] args) {
// 返回虚拟机视图使用的最大内存
long maxMemory = Runtime.getRuntime().maxMemory();
// 返回虚拟机视图(JVM)使用的初始化总内存
long totalMemory = Runtime.getRuntime().totalMemory();
System.out.println("maxMemory=" + maxMemory + "字节\t" + (maxMemory / (double) 1024 / 1024) + "MB");
System.out.println("totalMemory=" + totalMemory + "字节\t" + (totalMemory / (double) 1024 / 1024) + "MB");
// 默认情况下,分配的总内存是电脑内存的1/4,而初始化内存:1/64
//OOM
//1.尝试扩大堆内存看结果
//2.分析内存,看一下哪个地方出现问题(专业工具)
//-Xms1024m -Xmx1024m -XX:+PrintGCDetails
// 305664+699392=1005056k=981.5M
}
}
package com.newer;
import java.util.Random;
public class Demo2 {
public static void main(String[] args) {
String str="javaniubibujieshi";
//-Xms8m -Xmx8m -XX:+PrintGCDetails
while (true){
str=str+new Random().nextInt(888888888)+new Random().nextInt(888888888);
}
}
}
元空间;逻辑上存在,物理上不存在
在一个项目中,突然出现OOM故障,如何排除
- 能够看到代码第几行出错:内存快照分析工具:MAT,Jprofiler
- Dubug:一行行分析代码
MAT,Jprofiler作用:
- 分析Dump内存文件,快速定位内存泄露
- 获得堆中的数据
- 获得大的对象
4.IDEA集成JProfiler性能分析工具
Plugins-->marketplace-->安装JProfiler
官网下载JProfiler客户端--注意注册
JProfiler 9.2 注册码
- L-Larry_Lau@163.com#23874-hrwpdp1sh1wrn#0620
- L-Larry_Lau@163.com#36573-fdkscp15axjj6#25257
- L-Larry_Lau@163.com#5481-ucjn4a16rvd98#6038
- L-Larry_Lau@163.com#99016-hli5ay1ylizjj#27215
- L-Larry_Lau@163.com#40775-3wle0g1uin5c1#0674
- L-Larry_Lau@163.com#7009-14frku31ynzpfr#20176
- L-Larry_Lau@163.com#49604-1jfe58we9gyb6#5814
- L-Larry_Lau@163.com#25531-1qcev4yintqkj#23927
- L-Larry_Lau@163.com#96496-1qsu1lb1jz7g8w#23479
- L-Larry_Lau@163.com#20948-11amlvg181cw0p#171159
JProfiler 9.1.1 注册码 x86_x64
- L-Larry_Lau@163.com#36573-fdkscp15axjj6#25257
- L-Larry_Lau@163.com#7009-14frku31ynzpfr#20176
- L-Larry_Lau@163.com#49604-1jfe58we9gyb6#5814
- L-Larry_Lau@163.com#25531-1qcev4yintqkj#23927
- L-Larry_Lau@163.com#96496-1qsu1lb1jz7g8w#23479
- L-Larry_Lau@163.com#20948-11amlvg181cw0p#171159
- L-Larry_Lau@163.com#23874-hrwpdp1sh1wrn#0620
- L-Larry_Lau@163.com#36573-fdkscp15axjj6#25257
- L-Larry_Lau@163.com#5481-ucjn4a16rvd98#6038
- L-Larry_Lau@163.com#99016-hli5ay1ylizjj#27215
- L-Larry_Lau@163.com#40775-3wle0g1uin5c1#0674
JProfiler_windows-x64_9_0_3.exe 注册码/序列号
- L-Larry_Lau@163.com#36573-fdkscp15axjj6#25257(亲测可用)
- L-Larry_Lau@163.com#5481-ucjn4a16rvd98#6038
- L-Larry_Lau@163.com#99016-hli5ay1ylizjj#27215
- L-Larry_Lau@163.com#40775-3wle0g1uin5c1#0674
- L-Larry_Lau@163.com#7009-14frku31ynzpfr#20176
- L-Larry_Lau@163.com#49604-1jfe58we9gyb6#5814
- L-Larry_Lau@163.com#25531-1qcev4yintqkj#23927
- L-Larry_Lau@163.com#96496-1qsu1lb1jz7g8w#23479
- L-Larry_Lau@163.com#20948-11amlvg181cw0p#171159
安装好JProfiler客户端后,
这样IDEA集成JProfiler就可以了
package com.newer;
//-Xms 设置初始化内存分配大小 1/64
//-Xmx 设置最大分配内存,默认1/4
//-XX:+HeapDumpOnOutOfMemoryError oom Dump
//-XX:+PrintGCDetails 打印GC垃圾回收信息
//-Xms1m -Xmx8m -XX:+HeapDumpOnOutOfMemoryError
//生成Dump文件
import java.util.ArrayList;
public class Demo3 {
byte[] array = new byte[1024];
public static void main(String[] args) {
ArrayList<Object> list = new ArrayList<>();
int count=0;
try {
while(true){
list.add(new Demo3());
count=count+1;
}
} catch (Exception e) {
System.out.println("count"+count);
}
}
}
找到当前项目的父级目录,利用JProfiler打开
在Thread Dump中就可以知道哪行出问题
标签:之堆,Survivor,Larry,内存,JVM,Lau,com,详解,163 来源: https://blog.csdn.net/weixin_44364444/article/details/110258934