JVM 笔记

Posted 皓洲

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JVM 笔记相关的知识,希望对你有一定的参考价值。

JVM 笔记

JVM里面含有什么东西?

方法区、堆、Java栈、本地方法栈、程序计数器。

Java的垃圾在哪里产生?

方法区和堆中产生。

类加载器

  1. 虚拟机自带的类加载器
  2. 启动类(根)加载器
  3. 扩展类加载器 ExtClassLoader
  4. 应用程序加载器 AppClassLoader

双亲委培机制

类加载机制:BOOT(优先执行)—>EXC—>APP

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

类加载机制

沙箱安全机制

​ 什么是沙箱?沙箱是一个限制程序运行时的环境。沙箱机制就是将java代码限定在虚拟机特定的运行范围中,并且限制代码堆本地系统资源访问,通过这样的措施来保证对代码的有效隔离,防止对本地提醒哦那个造成破坏。

​ 在Java中将执行程序分为本地代码和远程代码两种,本地代码默认视为可信任的,而远程代码泽被看做是不受信任的。对于受信任的代码,可以访问本地一切资源。而对于非受信任的代码在早期Java实现中,安全依赖于沙箱机制。

​ 在Java1.2版本中,再次改进了安全机制,增加了代码签名,不论本地代码还是远程代码,都会按照用户的安全策略设定,由类加载器加载到虚拟机中权限不同的运行空间,来实现差异化的代码执行权限控制。

image-20210711164307810

​ 当前最新的安全机制实现,则引入了域(Domain)的概念。虚拟机会把所有代码加载到不同的系统域和应用域,系统域部分专门负责和关键资源进行交互,而哥哥应用域部分则通过系统域的部分代理来堆各种需要的资源进行访问。虚拟机中不同的受保护域(Protected Domain),对应不一样的权限(Permission)。纯在于不同域中的类文件就具有了当前域的全部权限。

image-20210711164906888

组成沙箱的基本组件

  • 字节码检验其:确保Java类文件遵循Java语言规范。但不是所有类文件都会经过字节码校验,如:核心类。
  • 类加载器:其中类加载器在三个方面对Java沙箱起作用
    • 他防止恶意代码干涉正常的代码
    • 守护了被信任类的库边界
    • 将代码归入保护域,确定了代码可以进行哪些操作。

native关键字

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

会进入本地方法栈

调用本地方法接口 JNI

JNI作用:扩展Java的使用,融合不同的编程语言为Java所用! 最初:C、C++。

Java诞生的时候 C、C++横行,想要立足,必须有调用C、C++的程序

他在内存区域中专门开辟了一块标记区域:Native Method Stack,登记native方法

他最终在执行的时候,加载本地方法库中的方法通过JNI。

现在调用其他接口: Socket,webService,http

如何判断对象是否死亡(两种方法)

引用计数法

​ 给对象中添加一个引用计数器,每当有一个地方引用它,计数器就加 1;当引用失效,计数器就减 1;任何时候计数器为 0 的对象就是不可能再被使用的。

这个方法实现简单,效率高,但是目前主流的虚拟机中并没有选择这个算法来管理内存,其最主要的原因是它很难解决对象之间相互循环引用的问题。 所谓对象之间的相互引用问题,如下面代码所示:

​ 除了对象 objA 和 objB 相互引用着对方之外,这两个对象之间再无任何引用。但是他们因为互相引用对方,导致它们的引用计数器都不为 0,于是引用计数算法无法通知 GC 回收器回收他们。

public class Gc { 
    Object instance = null; 
    public static void main(String[] args) { 
        Gc A = new Gc(); 
        Gc B = new Gc(); 
        A.instance = B; 
        B.instance = A; 
        A = null; 
        B = null; 
    } 
}

可达性分析算法

​ 这个算法的基本思想就是通过一系列的称为 “GC Roots” 的对象作为起点,从这些节点开始向下搜索,节点所走过的路径称为引用链,当一个对象到 GC Roots 没有任何引用链相连的话,则证明此对象是不可用的。

image-20210701160814497

简单的介绍一下强引用、软引用、弱引用、虚引用

无论是通过引用计数法判断对象引用数量,还是通过可达性分析法判断对象的引用链是否可达,判定对象的存活都与“引用”有关。

1.强引用(StrongReference)

​ 以前我们使用的大部分引用实际上都是强引用,这是使用最普遍的引用。如果一个对象具有强引用,那就类似于必不可少的生活用品,垃圾回收器绝不会回收它。当内存空间不足,Java 虚拟机宁愿抛出 OutOfMemoryError 错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足问题。

2.软引用(SoftReference)

​ 如果一个对象只具有软引用,那就类似于可有可无的生活用品。如果内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。

​ 软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收,JAVA 虚拟机就会把这个软引用加入到与之关联的引用队列中。

3.弱引用(WeakReference)

​ 如果一个对象只具有弱引用,那就类似于可有可无的生活用品。弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程, 因此不一定会很快发现那些只具有弱引用的对象。

​ 弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java 虚拟机就会把这个弱引用加入到与之关联的引用队列中。

4.虚引用(PhantomReference)

​ “虚引用”顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。

虚引用主要用来跟踪对象被垃圾回收的活动

虚引用与软引用和弱引用的一个区别在于: 虚引用必须和引用队列(ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。程序如果发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。

​ 特别注意,在程序设计中一般很少使用弱引用与虚引用,使用软引用的情况较多,这是因为软引用可以加速 JVM 对垃圾内存的回收速度,可以维护系统的运行安全,防止内存溢出(OutOfMemory)等问题的产生

如何判断一个常量是废弃常量

​ 运行时常量池主要回收的是废弃的常量。那么,我们如何判断一个常量是废弃常量呢?

​ 假如在常量池中存在字符串 “abc”,如果当前没有任何 String 对象引用该字符串常量的话,就说明常量 “abc” 就是废弃常量,如果这时发生内存回收的话而且有必要的话,”abc” 就会被系统清理出常量池。

垃圾收集算法

标记-清除算法

​ 该算法分为“标记”和“清除”阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。它是最基础的收集算法,后续的算法都是对其不足进行改进得到。这种垃圾收集算法会带来两个明显的问题:

  1. 效率问题
  2. 空间问题(标记清除后会产生大量不连续的碎片)

image-20210701163753466

复制算法

​ 为了解决效率问题,“复制”收集算法出现了。它可以将内存分为大小相同的两块,每次使用其中的一块。当这一块的内存使用完后,就将还存活的对象复制到另一块去,然后再把使用的空间一次清理掉。这样就使每次的内存回收都是对内存区间的一半进行回收。

image-20210701164218647

标记-整理算法

​ 根据老年代的特点提出的一种标记算法,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象回收,而是让所有存活的对象向一端移动,然后直接清理掉端边界以外的内存。

image-20210701164300629

分代收集算法

​ 当前虚拟机的垃圾收集都采用分代收集算法,这种算法没有什么新的思想,只是根据对象存活周期的不同将内存分为几块。一般将 java 堆分为新生代和老年代,这样我们就可以根据各个年代的特点选择合适的垃圾收集算法。

比如在新生代中,每次收集都会有大量对象死去,所以可以选择复制算法,只需要付出少量对象的复制成本就可以完成每次垃圾收集。而老年代的对象存活几率是比较高的,而且没有额外的空间对它进行分配担保,所以我们必须选择“标记-清除”或“标记-整理”算法进行垃圾收集。

以上是关于JVM 笔记的主要内容,如果未能解决你的问题,请参考以下文章

学习笔记:python3,代码片段(2017)

分析定位占用CPU资源高的JVM线程

JVM学习笔记------Java代码编译和执行的整个过程

一篇笔记整理JVM工作原理

一篇笔记整理JVM工作原理

一篇笔记整理JVM工作原理