JVM架构、JVM垃圾回收机制、垃圾回收算法、垃圾回收器、JMM(内存模型)

0 JVM和Java的关系

  • JDK = JRE + Java开发工具(java,javac,javadoc,javap…)
  • JRE = JVM + Java核心类库
    即: JDK = JVM + Java核心类库 + Java开发工具

1 JVM架构(JVM内存区域)

1-1 JVM位置在哪里?

  • 硬件——>OS——>JVM
  • 即:JVM在操作系统之上。

1-2 JVM架构

  • 分三大块:
    • Class Loader Subsystem 类加载子系统
    • Runtime Data Area 运行时数据区
    • Execution Engine 执行引擎
  • 画图:在这里插入图片描述

1-3 类加载器

  • 位置:在类加载子系统。
  • 类加载过程:加载——>链接(验证、准备、解析)——>初始化
  • 双亲委派机制:保证类加载和程序的安全。
    • 1)类加载器收到类的请求
    • 2)将这个请求向上委托给父类,一直向上委托(此时在最老)。(加载器:APP->EXC->BOOT)
    • 3)启动加载器检查是否能加载当前类,能就结束,并使用当前加载器;不能就抛异常通知子加载器进行加载
    • 4)重复3
    • 总结:在继承关系中,最老的类优先选择。

1-4 沙箱安全机制

  • 作用:Java安全的核心,限制程序运行环境。

1-5 栈stack

  • 特点:先进后出,后进先出。
  • 作用:主管程序的运行,生命周期和线程同步。线程结束栈内存就释放,不存在垃圾回收问题。
  • 栈存什么?
    • 八大基本数据类型
    • 对象的引用
    • 实例的方法

1-6 方法区

  • 特点:被所有线程共享。
  • 方法区存什么?
    • 所有字段和方法字节码。
    • 静态变量,常量,类信息,即时编译器编译后放入的代码。

1-7 PC寄存器

  • 每个线程都有一个程序计数器,是线程私有的,就会一个指针(我们接触不到),指向方法区中的方法字节码,在执行引擎读取下,加一。
  • 它占用的内容空间几乎忽略不计。

1-8 堆heap(需要垃圾回收)

  • 分区:
    • 1.8之前:新生区(伊甸园区,幸存区),老年区
    • 1.8之后:
  • 堆中存什么?
    • 对象实例和数组。

2 JVM垃圾回收机制——针对 堆

2-1 如何判断对象是不是垃圾?

2-1-1 引用计数法

  • 用计数器记录对象的引用次数,为0的就是垃圾。
  • 缺点:难以解决对象之间相互循环引用问题,所以不用。
  • PS:现在基本不用了。

2-2-2 可达性算法

  • 将GC Roots对象作为起点,从这些节点向下搜索引用的对象,找到对象都标记为非垃圾对象,其余是垃圾对象。
  • GC Roots根节点:线程栈的本地变量,静态变量本地方法栈的变量等等。
  • 优点:解决了引用计数法的对象之间相互循环引用问题。
    在这里插入图片描述

3 垃圾回收算法

3-1 复制算法

  • gc时把依然存活的对象复制到两个幸存区之一的to幸存区域(谁空谁是to)。当达到15次(默认)时,认为这个对象有很强的生命力,放到
  • 缺点:浪费一个幸存区空间。
  • 优点:不产生碎片。

3-2 标记清除算法

  • 直接回收垃圾对象,保留存活对象。
  • 位置不连续,产生碎片。

3-3 标记整理算法/标记压缩算法

  • 回收之后,整理空间。
  • 优点:不产生碎片。
  • 缺点:效率变低。

4 垃圾回收器(10种…)

请添加图片描述

5 JMM(Java内存模型)

  • JMM定义了线程工作内存与主内存(线程之间的共享变量)的抽象关系。并不真实存在,它描述的是一组规则或规范,通过这组规范定义了程序中各个变量(包括实例字段,静态字段和构成数组对象的元素)的访问方式。
  • JVM运行程序的实体是线程,而每个线程创建时JVM都会为其创建一个工作内存(有些地方称为栈空间),用于存储线程私有的数据。Java内存模型中规定所有变量都存储在主内存,主内存是共享内存区域,所有线程都可以访问。
  • 首先要将变量从主内存拷贝的自己的工作内存空间,然后对变量进行操作,操作完成后再将变量写回主内存,不能直接操作主内存中的变量,工作内存中存储着主内存中的变量副本拷贝,前面说过,工作内存是每个线程的私有数据区域,因此不同的线程间无法访问对方的工作内存,线程间的通信(传值)必须通过主内存来完成。

在这里插入图片描述

  • JMM 与 Java内存区域 唯一相似点,都存在共享数据区域和私有数据区域:
    • 在JMM中主内存属于共享数据区域,从某个程度上讲应该包括了方法区;而工作内存数据线程私有数据区域,从某个程度上讲则应该包括程序计数器虚拟机栈以及本地方法栈
      • 此处为什么包括本地方法栈?本地方法栈中的本地方法是怎么被线程调用的?:Java虚拟机栈用于管理Java方法的调用,而本地方法栈用于管理本地方法的调用。本地方法栈,也是线程私有的。如果线程请求分配的栈容量超过本地方法栈允许的最大容量,Java虚拟机将会抛出一个 StackOverflowError 异常。如果本地方法栈可以动态扩展,并且在尝试扩展的时候无法申请到足够的内存,或者在创建新的线程时没有足够的内存去创建对应的本地方法栈,那么Java虚拟机将会抛出一个 OutOfMemoryError 异常。本地方法栈是线程私有就是因为 只有线程调用时,线程的本地方法栈才会登记本地方法。
      • 它的具体做法是Native Method Stack中登记native方法,在Execution Engine 执行时加载本地方法库。
      • 当某个线程调用一个本地方法时,它就进入了一个全新的并且不再受虚拟机限制的世界。它和虚拟机拥有同样的权限。
        • 本地方法可以通过本地方法接口来访问虚拟机内部的运行时数据区。
        • 它甚至可以直接使用本地处理器中的寄存器。
        • 直接从本地内存的堆中分配任意数量的内存。
    • 或许在某些地方,我们可能会看见主内存被描述为堆内存,工作内存被称为线程栈,实际上他们表达的都是同一个含义。
      个人理解:JMM只是用线程角度思考JVM;JVM架构(JVM内存区域)就是从物理角度。
  • 关于JMM中的主内存和工作内存说明如下:
    • 主内存 存什么?:主要存储的是Java所有线程创建的实例对象(不管这个实例对象是成员变量还是方法中的本地变量(也叫局部变量)),当然也包括了类信息、常量、静态变量。
    • 工作内存 存什么?:主要存储当前方法的所有本地变量信息(针对本线程说)(主内存中变量的拷贝副本),每个线程这能访问自己的工作内存(即线程中的本地变量对其他线程是不可见的,就算是两个线程执行同一段代码,他们也会各自在自己的工作内存中胡藏剑属于当前线程的本地变量,当然也包括了字节码行号指示器,相关Native方法的信息),由于工作内存是每个线程的私有数据,线程间无法相互访问工作内存,
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值