为了能到远方,脚下的每一步都不能少.|
2023-08-17 19:50阅读: 7评论: 0推荐: 0

JVM

JVM

1. JVM的位置

  1. 运行在操作系统上
    image

2. jvm的体系结构

image

3. 类加载器

  1. 作用:加载class文件

  2. 虚拟机自带的加载器

    启动类(根)加载器:JAVA_HOME\lib\jar包 or rt.jar

    扩展类加载器:JAVA_HOME\lib\ext

    应用程序(用户)加载器:classpath

4. 双亲委派机制

为了保证安全:自定义-->app->ext->bootstrap

使用不同的类加载器最终得到的都是同样一个 Object 对象

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

5. 沙箱安全机制

  1. java安全模型的核心是java沙箱
  2. 主要限制系统资源(CPU、内存、文件系统、网络)的访问。
  3. 不同级别的沙箱对系统资源访问的限制也有差异。

6. Native

凡是带了native关键字的,说明java的作用范围达不到了,回去调用底层C语音的库

7. PC寄存器

  1. 每条线程都要有一个独立的程序计数器,这类内存也称为“线程私有”的内存,就是一个指针,指向方法区的方法字节码。
  2. 程序计数器是每个线程私有的。存储的数据所占空间的大小不会随程序的执行而发生改变,所以不会发生内存溢出

8. 方法区

  1. 类信息(构造方法、接口定义)、常量、静态变量、即时编译器编译后的代码

static/final/Class/常量池

  1. 如果线程正在执行得是一个Java方法,计数器记录的是正在执行得虚拟机字节码指令的地址
  2. 如果正在执行的是Nativa方法,这个计数器值则为空
  3. jdk7叫作永久代,jdk8叫作元空间

9. 栈

  1. 数据结构

  2. 程序 = 数据结构 + 算法

  3. 栈:先进后出,后进先出

    队列:先进先出(FIFO:first input first output)

  4. 每个方法在执行的同时都会创建一个栈帧

  5. 主管程序的运行,生命周期与线程同步,一旦线程结束,栈就OVER

  6. 存放基本数据类型、对象引用
    image

10. 堆

  1. 一个JVM只有一个堆内存,堆内存大小可以调节
  2. 类、方法、常量、变量、引用类型的真实对象
  3. 所有的对象实例以及数组都要在堆上分配

10.1 细分

image

10.1.1 新生代

  1. 一般占据堆的1/3空间
  2. 由于频繁创建对象,新生代会频繁触发MinorGC进行垃圾回收
  3. 新生代又分为:Eden ServivorFrom ServivorTo

10.1.1.1 Eden

  • 新建对象的出生地
  • 如果新创建的对象占用内存很大,则直接分配到老年代)
  • 当 Eden 区内存不够的时候就会触发 MinorGC,对新生代区进行一次垃圾回收

10.1.1.2 ServivorFrom

  • 上一次 GC 的幸存者,作为这一次 GC 的被扫描者

10.1.1.3 ServivorTo

  • 保留了一次 MinorGC 过程中的幸存者

10.1.2 MinorGC

  1. 复制

​ eden、servicorFrom复制到 ServicorTo,年龄+1

  • 首先,把 Eden 和 ServivorFrom 区域中存活的对象复制到 ServicorTo 区域(如果有对象的年
    龄以及达到了老年的标准,则赋值到老年代区)

  • 同时把这些对象的年龄+1(如果 ServicorTo 不够位置了就放到老年区)

  1. 清空

​ 清空eden、servicorFrom

  • 清空 Eden 和 ServicorFrom 中的对象
  1. 互换

​ ServicorTo和 ServicorFrom互换

  • ServicorTo 和 ServicorFrom 互换,原 ServicorTo 成为下一次 GC 时的 ServicorFrom

10.1.3 老年代

  1. 存放应用程序中生命周期长的内存对象
  2. 老年代的对象比较稳定,所以 MajorGC 不会频繁执行
  3. 在进行 MajorGC 前一般都先进行了一次 MinorGC,使得有新生代的对象晋身入老年代,导致空间不够用时才触发
  4. 当无法找到足够大的连续空间分配给新创建的较大对象时也会提前触发一次 MajorGC 进行垃圾回收腾出空间

10.1.3 MajorGC

  1. 扫描一次所有老年代,标记出存活的对象
  2. 回收没有标记的对象
  3. MajorGC 的耗时比较长,因为要扫描再回收
  4. MajorGC 会产生内存碎片,为了减少内存损耗,我们一般需要进行合并或者标记出来方便下次直接分配。
  5. 当老年代也满了装不下的时候,就会抛出 OOM异常

10.1.4 永久代

  1. 存放jdk自身携带的class对象。接口元数据,存储的是Java运行时的一些环境或者类信息

  2. Class 在被加载的时候被放入永久区域,它和和存放实例的区域不同

  3. GC 不会在主程序运行期对永久区域进行清理,导致了永久代的区域会随着加载的 Class 的增多而胀满,最终抛出 OOM 异常

  4. jak1.6之前:永久代,常量池在方法区

    jdk1.7:永久代,但慢慢退化了,常量池在堆中

    jdk1.8之后:无永久代,常量池在元空间

11. 常见异常

  1. OutOfMemoryFrror:内存溢出
  2. StackOverflowError:栈溢出

12. 类加载过程

12.1 加载

把class文件加载到内存中

  1. 根据类名,找到class文件
  2. 把class文件加载到内存
  3. 创建一个类对象

12.2 验证

确保class文件的字节流包含的消息是否符合JVM标准,是否危害到虚拟机自身的安全

12.3 准备

给静态变量赋0值

12.4 解析

初始化常量(虚拟机将常量池的符号引用替换为直接引用的过程)

12.5 初始化

执行构造方法

13. 垃圾回收

13.1 判断对象是死亡对象

13.1.1 引用计数法

  1. 给对象加一个引用计数器
  2. 每当有一个地方引用它,计数器+1
  3. 引用失效,计数器-1
  4. 当计数器为0时,为死亡对象

优点:

  1. 简单

缺点:

  1. 会浪费一部分空间用来计数,加入一个空间对象是2个字节,计数的空间为4个字节
  2. 不能解决循环引用问题

13.1.2 可达性分析法

  1. 通过一系列的“GC roots”对象作为起点搜索
  2. 如果在“GC roots”和一个对象之间没有可达路径,则称该对象是不可达的
  3. 不可达对象不等价于可回收对象,不可达对象变为可回收对象至少要经过两次标记过程
  4. 两次标记后仍然是可回收对象,则将面临回收

13.2 垃圾回收法

13.2.1 复制算法

  1. 将内存分为容量大小相等的两个,分别是From区和To区,新创建的对象都放在From区
  2. 当From区的内存不够存储下一个对象的时候,就将From区中存活的对象复制到To区,然后再将From区一次性全部清空回收,此时From区和To区的职责发生变化,原来的From区变为To区,原来的To区变为From区

优点:

  1. 有效解决内存碎片的问题

缺点:

  1. 当对象的存活率较高的时候使用这种算法的效率较低
  2. 内存空间资源的浪费

13.2.2 标记清除法

标记出所有需要回收的对象,回收被标记的对象所占用的空间

优点:

  1. 算法思路简单清晰

缺点:

  1. 效率低下
  2. 需要维护空间碎片

13.2.3 标记压缩法

标记后不是清理对象,而是将存活对象移向内存的一端。然后清除端边界外的对象

优点:

  1. 避免了内存碎片的产生

缺点:

  1. 对于内存中存活对象较多的时候,使用这种算法需要做大量的移动工作,效率性能低下

13.2.4 分代法

根据对象存活的不同生命周期将内存划分为不同的域,一般情况下将 GC 堆划分为新生代和老年代,持久代。

1. 新生代

  1. 新生代的特点是每次垃圾回收时都有大量垃圾需要被回收。

  2. 大部分新生的对象都放在新生代中,该代中对象的存活时间较短,GC频次较高,使用复制算。

  3. 新生代分为3个区:一个Eden区和两个Survivor区。

  4. 当Eden区满了之后,将存活的对象复制到任意一个Survivor区,当其中一个Servicor区满了之后,将这个区中存活的对象复制到另外一个Survivor区中,当这个Survivor区也满了之后,从Survivor区A中复制过来的对象如果还存活着就将他复制到老年代中(需要注意的是Survivor区既存在从Eden区过来的对象,也存在从另外一个Survivor区复制过来的对象,总有一个Survivor区是空的,Survivor区是可以配置多个的(多于2个),这样做的目的是为了让对象尽可能的存在于新生代,也就是延长对象在新生代的存在时间)

2. 持久代

  1. JAVA 虚拟机提到过的处于方法区的永久代(Permanet Generation),它用来存储 class 类,常量,方法描述等
  2. 对永久代的回收主要包括废弃常量和无用的类。

3. 老年代

  1. 对象的内存分配主要在新生代的 Eden Space 和 Survivor Space 的 From Space(Survivor 目
    前存放对象的那一块),少数情况会直接分配到老年代。

  2. 当新生代的 Eden Space 和 From Space 空间不足时就会发生一次 GC,进行 GC 后,Eden
    Space 和 From Space 区的存活对象会被挪到 To Space,然后将 Eden Space 和 From
    Space 进行清理。

  3. 如果 To Space 无法足够存储某个对象,则将这个对象存储到老年代。

  4. 在进行 GC 后,使用的便是 Eden Space 和 To Space 了,如此反复循环。

  5. 当对象在 Survivor 区躲过一次 GC 后,其年龄就会+1。默认情况下年龄到达 15 的对象会被
    移到老年代中。

13.2.5 总结

内存整齐度:复制算法=标记压缩算法>标记清除算法

内存效率(时间复杂度):复制算法>标记清除算法>标记压缩算法

内存利用率:标记压缩算法=标记清除算法>复制算法

本文作者:Morita-desin

本文链接:https://www.cnblogs.com/yqquinn/p/17638719.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Morita-desin  阅读(7)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起