JVM 初步学习

本文详细介绍了JVM的工作原理,包括其在操作系统中的位置、体系结构、类加载器机制、双亲委派模型、沙箱安全、内存区域如栈、堆和方法区的细节,以及垃圾回收的算法和过程。同时,讨论了如何通过调整JVM参数进行内存调优,解决OOM问题,并分析了不同垃圾回收算法的优缺点。
摘要由CSDN通过智能技术生成

JVM 探究


JVM 的位置


  • 在操作系统之上,可以看做是一个在操作系统上运行的软件,上面跑的都是 java 程序

JVM 的体系结构


在这里插入图片描述

  • 栈用完系统会自动释放,所以不会存在垃圾

类加载器


  • 虚拟机自带的加载器

  • 启动类(根)加载器

    • BootClassLoader

    • C 语言编写的,无法直接获取

    • 所在位置:rt.jar

  • 扩展类加载器

    • ExtClassLoader
    • 所在位置:\jre\lib\ext
  • 应用程序(系统类)加载器

    • AppClassLoader

双亲委派机制

  1. 类加载器收到类加载的请求
  2. 将这个请求向上委托给类型加载器去完成,一直向上委托,直到启动类加载器
  3. 启动类加载器检查是否能够加载当前这个类,能加载就结束,使用当前的加载器,否则,抛出异常,通知子加载器进行加载
  4. 重复步骤3

沙箱安全机制


  • 沙箱是一个限制程序运行的环境
基本组件
  • 字节码校验器
    • 确保 java 类文件遵循 java 语言规范,并不是所有的类文件都会经过字节码校验,比如核心类
  • 类装载器
    • 防止恶意代码区干涉善意代码 //双亲委派机制
    • 守护了被信任的类库边界
    • 将代码归入保护域,确定了代码可以进行哪些操作

native


  1. 凡是带了 native 关键字的,说明已经超出了 java 的作用范围,会去调用底层 C 语言的库

  2. 进入本地方法栈

  3. 调用本地方法接口(JNI:Java Native Interface)

  4. JNI 的作用:扩展 Java 的使用,融合不同的编程语言为 Java 所用

  • Java 在内存区域中专门开辟了一块标记区域:本地方法栈(Native Method Stack),登记 native 方法
  • 在最终执行时,通过 JNI 加载本地库中的方法

PC 寄存器


  • 程序计数器:Program Counter Register

  • 每个线程都有一个程序计数器,是线程私有的,是一个指针,指向方法区中的方法字节码,是一个非常小的内存空间,几乎可以忽略

    不计

  • 它包含当前正在执行的指令的地址(位置)。当每个指令被获取,程序计数器的存储地址加一。在每个指令被获取之后,程序计数器

    指向顺序中的下一个指令

方法区


  • 方法区本质是一种规范,并不存在

  • 所有定义的方法的信息都保存在该区域,此区域属于共享区域

  • 静态变量,常量,类信息(构造方法,接口定义),运行时的常量池存在方法区中,但是实例变量存在推内存中,和方法区无关


  • 一种数据结构

  • 程序=数据结构+算法

  • 栈:先进后出,后进先出

    队列:先进先出(FIFO:First Input First Output)

  • 栈内存主管程序运行,生命周期和线程同步,线程结束,栈内存被释放

  • 栈不存在垃圾回收问题

  • 栈满(调用的深度比栈深度深的时候出现)的错误:StackOverflowError

栈内包含

  • 8大基本类型
  • 对象的引用
  • 实例的方法


  • 一个 JVM 只有一个堆内存,堆内存的大小是可以调节的

分区

  • 99% 的对象都是临时对象
新生代(占堆内存的1/3)
  • 对象诞生,成长甚至死亡的地方
  • 采用轻 GC 回收
Eden(占新生代8/10)
  • 对象被 new 出来的地方
Survivor(占新生代2/10)
  • 分为两块,每次只使用一块
老年代(占堆内存的2/3)
  • 采用重 GC(Full GC)回收
对象在堆中的生命周期
  • new 的对象一般在新生代 Eden 区域中,Eden 区域满时,在 被使用的 Survivor 区域 new 对象

  • 当 Eden 区域对象数量达到上限时,触发轻 GC,清除掉当前新生代中没有被引用的对象,被引用的对象复制进未被使用的 Survivor

    区域

  • 对象在 Survivor 区域的初始年龄为1,每经历一次轻 GC(伴随着一次复制算法的执行) 年龄+1,当年龄到达默认值15(可以通过-

    XX:MaxTenuringThreshold修改参数)时,将此对象存入老年代

  • 当老年代对象数量达到上限时,触发重 GC(Full GC),清除掉当前新生代和老年代中没有被引用的对象

  • 当新生代和老年代都达到上限且没有可以清理的对象时,系统报错 OOM

永久区


永久区与堆不相连,但与堆共享物理内存,逻辑上可认为在堆中

这个区域是方法区在 JVM 中的实现

这个区域常驻内存,用来存放 JDK 自身携带的 Class 对象,Interface 元数据,存储的是 Java 运行时的一些环境或类信息

JDK1.7 及以前这个区域存在垃圾回收

JDK1.8 及以后这个区域不存在垃圾回收

关闭 JVM 虚拟机就会释放这个区域的内存

  • JDK1.6 之前:永久代,常量池在方法区
  • JDK1.7:永久代,常量池在堆中
  • JDK1.8:永久代变为元空间,常量池在元空间

内存调优


  • 默认情况下,初始化的内存是电脑内存的 1/64 ,分配的最大内存是电脑内存的 1/4

OOM 问题解决

  1. 尝试修改参数扩大堆内存看结果

    不报错,说明 OOM 是由内存不够引起的

    报错,进行下一步

    -Xms1024m -Xmx1024m -XX:+PrintGCDetails
    

    -XX:+PrintGCDetails 打印GC清理的垃圾信息

  2. 使用内存快照分析工具(MAT 或 Jprofiler)查看错误在哪一行

    Jprofiler 的作用

    • 分析 Dump 内存文件,快速定位内存泄漏

      -Xms1m -Xmx8m -XX:+HeapDumpOnOutOfMemoryError
      

      -Xms 设置初始化内存分配大小

      -Xmx 设置最大分配内存

      -XX:+HeapDumpOnOutOfMemoryError 获得 OOM 相关的 Dump 内存文件

    • 获得堆中的数据

    • 获得大的对象

GC:垃圾回收


  • GC 的作用区域只有方法区和堆

GC 分类

  • 轻 GC(Minor GC)

  • 重 GC(Full GC)

  • JVM 在进行 GC 时,并不是对 Eden 区,Survivor 区域,老年代甚至 JDK1.7 之前 永久代的统一回收。大部分时候,回收的都是 Eden

判断是否可回收

引用计数法
  • 每个对象中会添加一个计数器,当对象每新加一个引用时,计数器加1;当每有一个该对象的引用失效时,计数器减1,当计数器为0

    时代表对象没有被引用可以被回收

  • 计数器本身也会被消耗

  • 缺点

    • 需要额外的内存来存放计数器
    • 运行期间需要维护计数器,带来额外开销
    • 无法解决循环引用问题
可达性分析法(主流)

在这里插入图片描述

通过一系列被称为「GC Roots」的根对象作为起始节点集,从这些节点开始,通过引用关系向下搜寻,搜寻走过的路径称为「引用链」,如果某个对象到GC Roots没有任何引用链相连,就说明该对象不可达,即可以被回收。

  • Object1,2,3 之间存在引用,且 GC ROOTS 可达,因此不会被回收
  • Object4,5,6 之间存在引用,但由于 GC ROOTS 不可达,因此会被回收
  • GC ROOTS 就是对象
  • 可以作为 GC ROOTS 的对象
    • 方法区静态属性引用的对象
    • 方法区常量池引用的对象
    • 方法栈中栈本地变量表引用的对象
    • JNI 本地方法栈中引用的对象
    • 获得同步锁的对象

GC 的算法

复制算法

复制算法最佳使用场景:对象存活率较低的时候,新生代

在这里插入图片描述

  • 当 Eden 区域第一次发生轻 GC 时,存活的对象复制进未被使用的 Survivor 区域,清理 Eden 区域,该 Survivor 区域变为被使用的

    Survivor 区域

  • 当再次发生轻 GC 时,将 Eden 区域存活的对象复制进未被使用的 Survivor 区域,将被使用的 Survivor 区域中存活的对象复制到未

    被使用的 Survivor 区域中,清理 Eden 区域和被使用的 Survivor 区域,该 Survivor 区域变为被使用的Survivor 区域

  • 被使用的 Survivor 区域被称为 From Survivor 区域,未被使用的 Survivor 区域被称为 To Survivor 区域,To Survivor 区域始终为空

优点

  • 没有内存碎片

缺点

  • 浪费了内存空间(一半的 Survivor 区域)
标记清除法

标记:扫描内存中的对象,对所有被引用的对象进行标记

清除:扫描内存中的对象,对没有标记的对象进行清除

在这里插入图片描述

优点

  • 不需要额外的空间

缺点

  • 两次扫描,严重浪费时间
  • 会产生大量内存碎片
标记整理(压缩)法

标记清除法的优化

标记:扫描内存中的对象,对所有被引用的对象进行标记

清除:扫描内存中的对象,对没有标记的对象进行清除

压缩:将所有存货的对象压缩到内存的一端,按顺序排放,之后清理边界外所有空间

在这里插入图片描述

优点

  • 不需要额外的空间
  • 没有内存碎片产生

缺点

  • 效率低
三种算法的比较
复制算法标记清除法标记整理(压缩)法
算法效率(时间复杂度)
内存整齐度
内存利用率
哪种算法最好

没有最好的算法,只有适合的算法

GC:分代收集算法

新生代

  • 存活率低,复制算法

老年代

  • 区域大,存活率高
  • 标记清除法(内存碎片不是太多)+ 标记整理(压缩)法混合实现
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值