JVM深入学习

01_JVM概述和类装载器

问题:
① 请谈谈你对JVM的理解?java8的虚拟机有什么更新?
② 什么是 oom ? 什么是StackOverflowError?有哪些方法分析?
③ JVM的常用参数调优你知道哪些?
④ 内存快照抓取和MAT分析 DUMP 文件知道哪些?
⑤ 谈谈 JVM 中,对类加载器你的认识是?

PAST1 JVM 体系结构概述:

① JVM 是运行在操作系统之上的,它与硬件没有直接的交互
② JVM 体系结构概览:
在这里插入图片描述
a. 灰色区域绝对没有垃圾分类
b. 调优 (堆)

类装载器(ClassLoader):

1.负责加载 class 文件,class 文件在文件开头有特定的文件标示,并且 ClassLoader 只负责 class 文件的加载,至于它是否可以运行,则由 Execution Engine 决定

2.类加载器种类:
虚拟机自带的加载器:
① 启动类加载器(Bootstrap)c++
② 扩展类加载器 (Extension) Java
③ 系统类加载器(AppClassLoader) Java
也称应用程序类加载器 加载当前应用的classpath 的所有类
用户自定义加载器:
Java.lang.ClassLoader的子类,用户可以 制定类的加载方式
在这里插入图片描述

Execution Engine 执行引擎:

在这里插入图片描述
在这里插入图片描述
打印结果:
在这里插入图片描述

02_Native:

JNI:Java Native Interface
在这里插入图片描述

03_PC寄存器:

每个线程都有一个程序计数器,是线程私有的,就是一个指针,指向方法区中的方法字节码(用来存储指向下一条指令的地址,也即将要执行的指令代码),由执行引擎读取下一条指令,是一个非常小的内存空间,几乎可以忽略不记

04_方法区:

方法区是被所有线程共享,所有字段和方法字节码,以及一些特殊方法如构造函数,接口代码也在此定义。简单说,所有定义的方法的信息都保存在该区域,此属于共享区间。
静态变量 + 常量 + 类信息(构造方法/接口定义)+ 运行时常量池存在方法区中
但是
实例变量存在堆内存中,跟方法区无关!!!

在这里插入图片描述

05_栈:

① 栈是什么?在这里插入图片描述
② 栈存储是什么?栈存储是什么
③ 栈的运行原理:栈的运行原理
④ Java Stack:

Java Stack
图示在一个栈中有两个栈帧:
栈帧2是最先被调用的方法,先入栈,
然后方法2又调用方法1,栈帧1处于栈顶的位置,
栈帧2处于栈底,执行完毕后,依次弹出栈帧1和栈帧2,
线程结束,栈释放。

每执行一个方法都会产生一个栈帧,保存到栈(先进后出)的顶部,顶部栈就是当前的方法,该方法执行完毕后会自动将此栈帧出栈

⑤ StackOverflowErrorStackOverflowError
⑥ 栈 + 堆 + 方法区的交互关系

栈 + 堆 + 方法区的交互关系
HotSpot 是使用指针的方式来访问对象:

Java堆中会存放访问 类元数据 的地址,reference 存储的就直接是对象的地址

三种 JVM

  • Sun 公司的 HotSpot
  • BEA 公司的 JRockit
  • IBM公司的 J9 VM

PAST2 堆体系结构概述:

Heap 堆
1、 一个 JVM 实例只存在一个堆内存,堆内存的大小是可以调节的。类加载器读取了类文件后,需要把类、方法、常变量放到堆内存中,保存所有引用类型的真实信息,以便执行器执行,堆内存分为三部分*:
  • Young Generation Space 新生区 Young/New
  • Tenure generation Space 养老区 Old/Tenure
  • Permanent Space 永久区 Perm
2、 Heap 堆 (Java7之前)

一个JVM实例只存在一个堆内存,堆内存的大小是可以调节的。类加载器读取了类文件后,需要把类、方法、常变量放到堆内存中,保存所有引用类型的真实信息,以方便执行器执行。
Heap 堆

3、新生区 新生区

如果出现了 java.lang.OutOfMemoryError:Java heap space 异常,说明 Java 虚拟机的堆内存不够。原因有二:

(1)Java 虚拟机的堆内存设置不够,可以通过参数 -Xms、-Xmx 来调整。
(2) 代码中创建了大量的大对象,并且长时间不能被垃圾收集器收集(存在被引用)

4、Sun HotSpot 内存管理

Sun HotSpot 内存管理
① 新生代
新生代
② 旧生代
旧生代
③ 永久区
永久存储区是一个常驻内存区域,用于存放JDK自身所携带的 Class,Interface 的元数据,也就是说它存储的是运行环境必须的类信息,被转载进此区域的数据是不会被垃圾回收器回收掉的,关闭 JVM 才会释放此区域所占用的内存。

如果出现 java.lang.OutOfMemoryError:PermGen space,说明是Java虚拟机对永久代 Perm 内存设置不够。一般出现这种情况,都是程序启动需要加载大量的第三方 jar包。例如:在一个 Tomcat 下部署了太多的应用。或者大量动态反射生成的类不断被加载,最终导致 Perm 区被占满。

  • Jdk 1.6及以前:有永久代,常量池1.6在方法区
  • Jdk 1.7:有永久代,但已逐步“去永久代”,常量池1.7在堆
  • Jdk 1.8及之后:无永久代,常量池 1.8 在元空间

(1)、实际而言,方法区(Method Area)和堆一样,是各个线程共享的内存区域,它用于存储虚拟机加载的:类信息+普通常量+静态常量+编辑器编译后的代码等等,虽然 JVM 规范将方法区描述为堆的一个逻辑部分,但它却还有一个别名叫做 Non-Heap(非堆),目的就是要和堆分开。

(2)、对于 HotSpot 虚拟机,很多开发者习惯将方法区称之为“永久代(Parmanent Gen)”,但严格本质上说两者不同,或者说使用永久代来实现方法区而已,永久代是方法区(相当于是一个接口interface)的一个实现jdk1.7 的版本中,已经将原来放在永久代的字符串常量池移走。

(3)、常量池(Constant Pool)是方法区的一部分,Class 文件除了有类的版本、字段、方法、接口等描述信息外,还有一项信息就是常量池,这部分内容将在类加载后进入方法区的运行时常量池中存放。
在这里插入图片描述

PAST3 堆参数调优入门:

  • JVM 垃圾收集(Java Garbage Collection)
  • 上集,本次均以JDK1.8+HotSpot为例
1. Java7

Java7

2. Java8

Java8

3.堆内存调用简介01
  • -Xms:设置初始分配大小,默认为物理内存的“1/64”
  • -Xmx:最大分配内存,默认为物理内存的“1/4”
  • -XX:+PrintGCDetails:输出详细的GC处理日志
public static void main(String[] args) throws UnknownHostException {

        long maxMemory = Runtime.getRuntime().maxMemory() ;//返回 Java 虚拟机试图使用的最大内存量。
        long totalMemory = Runtime.getRuntime().totalMemory() ;//返回 Java 虚拟机中的内存总量。
        System.out.println("MAX_MEMORY = " + maxMemory + "(字节)、" + (maxMemory / (double)1024 / 1024) + "MB");
        System.out.println("TOTAL_MEMORY = " + totalMemory + "(字节)、" + (totalMemory / (double)1024 / 1024) + "MB");

    }

打印结果:
MAX_MEMORY = 1892155392(字节)、1804.5MB
TOTAL_MEMORY = 128974848(字节)、123.0MB

堆内存调优简介02

发现默认的情况下分配的内存是总内存的“1/4”、而初始化的内存为“1/64”
MAX_MEMORY = 1388838912(字节)、1324.5MB
TOTAL_MEMORY = 93847552(字节)、89.5MB

vm参数: -Xms1024m -Xmx1024m -XX:+PrinGCDetails
在这里插入图片描述

堆内存调优简介03

此图为 java7
在这里插入图片描述
java8:在这里插入图片描述

        String str = "www.atguigu.com" ;
        while(true) 
        {
        str += str + new Random().nextInt(88888888) + new Random().nextInt(999999999) ;
        }

VM参数:-Xms8 -Xmx8m -XX:+PrintGCDtails
在这里插入图片描述

内存快照分析图

内存快照分析图

  • 内存快照分析插件下载
    官网访问地址:
    https://projects.eclipse.org/projects/tools.mat/downloads
    内存快照分析插件下载
  • 插件安装路径
    插件安装路径
    -XX:+HeapDumpOnOutOfMemoryError
    OOM时导出推到文件
    -Xms1m -Xmx8m -XX:+HeapDumpOnOutOfMemoryError

09_复制算法(Copying)

GC算法总体概述:

GC算法总体概述
JVM在进行GC时,并非每次都对上面三个内存区域一起回收的,大部分时候回收的都是指新生代。
因此GC按照回收的区域又分了两种类型,一种是普通GC(minor GC),一种是全局GC(major GC or Full GC)
普通GC(minor GC):只针对新生代区域的GC。
全局GC(major GC or Full GC):针对年代的GC,偶尔伴随对新生代的GC以及对永久代的GC。

算法

1.面试题:
面试题

2.算法:
① 引用计数法(基本不用)

代码示例:

public class RefCountGC{
    public Object instance = null;
    private byte[] bigSize = new byte[2*1024*1024];
    public static void main(String[] args){
          RefCountGC objA = new RefCountGC();
          RefCountGC objB = new RefCountGC();
          objA.instance = objB;
          objB.instance = objA;
          objA = null;
          objB = null;
          System.gc();
    }
}

在这里插入图片描述② 复制算法(Copying)

年轻代中使用的是Minor GC,这种GC算法采用的是复制算法(Copying)
在这里插入图片描述
在这里插入图片描述
-XX:MaxTenuringThreshold — 设置对象在新生代中存活的次数
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
因为Eden 区对象一般存活率较低,一般的,使用两块10%的内存作为空间和活动区间,而另外80%的内存,则是用来给新建对象分配内存的。一旦发生GC,将10%的from活动区间与另外80%中存活的eden对象转移到10%的to空闲区间,接下来,将之前90%的内存全部释放,以此类推。

复制算法它的缺点也是相当明显的。
1.它浪费了一半的内存
2.如果对象的存活率很高,我们可以极端一点,假设是100%存活,那么我们需要将所有对象都复制一遍,并将所有引用地址重置一遍。复制这一工作所花费的时间,在对象存活率达到一定程度时,将会变的不可忽视。所以从以上描述不难看出,复制算法要想使用,最起码对象的存活率要非常低才行,而且最重要的是,我们必须要克服50%内存的浪费。

③ 标记清除(Mark-Sweep)
1、老年代一般是由标记清除或者是标记清除与标记整理的混合实现
2、原理
在这里插入图片描述
文字说明:
在这里插入图片描述
3.优缺点:
在这里插入图片描述
④ 标记压缩(Mark-Compact)

1、老年代一般是由标记清除或者标记清除与标记整理的混合实现
2、标记压缩原理:
在这里插入图片描述

⑤ 标记清除压缩(Mark-Sweep-Compact)
在这里插入图片描述

总结:

内存效率:复制算法>标记清除算法>标记整理算法(此处的效率只是简单的对比时间复杂度,实际情况不一定如此)
内存整齐度:复制算法=标记整理算法>标记清除算法
内存利用率:标记整理算法=标记清除算法>复制算法

没有最好的算法,只有最合适的算法。
即为 分代收集算法
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值