其他分享
首页 > 其他分享> > JVM之堆详解

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