JVM

JVM

垃圾回收算法

标记清除算法

最基础的垃圾回收算法,分为两个阶段,标注和清除。标记阶段标记出所有需要回收的对象,清除阶段回收被标记的对象所占用的空间。如图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s1sPothM-1597890706272)(C:\Users\晨边\AppData\Roaming\Typora\typora-user-images\image-20200802171609944.png)]

从图中我们可以发现,该算法最大的问题是内存碎片化严重。后续可能发生大对象不能找到可用空间的问题。

复制算法

为了解决 Mark-Sweep 算法内存碎片化的缺陷而被提出的算法。按内存容量将内存划分为等大小的两块。每次只使用其中一块,当这一块内存满后将尚存活的对象复制到另一块上去,把已使用的内存清掉,如图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iWiWhL0C-1597890706275)(C:\Users\晨边\AppData\Roaming\Typora\typora-user-images\image-20200802171848730.png)]

这种算法实现简单,内存效率高。不易产生碎片,但是最大的问题是可用内存被压缩到原来的一半。切存活对象增多的话,Copying算法的效率会大大降低。

标记整理算法

结合了以上两个算法,为了避免缺陷而提出。标记阶段和 Mark-Sweep 算法相同,标记后不是清理对象,而是将存活对象移向内存的一端。然后清除端边界外的对象。如图:

不足:需要移动大量对象,处理效率低

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qU2Qlohv-1597890706278)(C:\Users\晨边\AppData\Roaming\Typora\typora-user-images\image-20200802172138570.png)]》

分代收集算法

分代收集法是目前大部分 JVM 所采用的方法,其核心思想是根据对象存活的不同生命周期将内存划分为不同的域,一般情况下将 GC 堆划分为老生代(Tenured/Old Generation)和新生代(Young Generation)。老生代的特点是每次垃圾回收时只有少量对象需要被回收,新生代的特点是每次垃圾回收时都有大量垃圾需要被回收,因此可以根据不同区域选择不同的算法。

  • 新生代使用:复制算法
  • 老年代使用:标记 - 清除 / 标记 - 整理 算法

Java的引用

强引用

把一个对象赋给一个引用变量,这个引用变量就是一个强引用。当一个对象被强引用变量引用时,它处于可达状态,不会被JVM回收。因此是造成Java内存泄露的主要原因之一。

Object obj = new Object();

软引用

软引用需要用 SoftReference 类来实现,对于只有软引用的对象来说,当系统内存足够时它不会被回收,当系统内存空间不足时它会被回收。软引用通常用在对内存敏感的程序中。

Object obj = new Object();
SoftReference<Object> sf = new SoftReference<Object>(obj);
obj = null;  // 使对象只被软引用关联

弱引用

弱引用需要用 WeakReference 类来实现,它比软引用的生存期更短,对于只有弱引用的对象来说,只要垃圾回收机制一运行,不管 JVM 的内存空间是否足够,总会回收该对象占用的内存。

Object obj = new Object();
SoftReference<Object> sf = new SoftReference<Object>(obj);
obj = null;  // 使对象只被软引用关联

虚引用

虚引用需要 PhantomReference 类来实现,它不能单独使用,必须和引用队列联合使用。

虚引用的主要作用是跟踪对象被垃圾回收的状态。(唯一目的是能在这个对象被回收时收到一个通知)

Object obj = new Object();
PhantomReference<Object> pf = new PhantomReference<Object>(obj, null);
obj = null;

被动引用

  • 通过子类引用父类的静态字段,不会导致子类初始化

  • System.out.println(SubClass.value);  // value 字段在 SuperClass 中定义
    
  • 通过数组定义引用类,不会触发此类的初始化。该过程会对数组类进行初始化,数组类是一个由虚拟机自动生成的,直接继承自Object的子类,其中包含了数组的属性和方法。

  • SuperClass[] sca = new SuperClass[10];
    
  • 常量在编译阶段会存入调用类的常量池中,本质上并没有直接引用到定义常量的类,因此不会触发定义常量的类初始化

  • System.out.println(ConstClass.HELLOWORLD);
    

GC垃圾收集器

垃圾收集,主要针对方法区进行。程序计数器,虚拟机栈和本地方法栈这三个区域属于线程私有,只存在于生命周期内,线程结束之后就会消失,因此不需要对这三个区域进行垃圾回收

新生代主要使用复制标记-清除垃圾回收算法

老年代主要使用标记-整理垃圾回收算法

  • Minor GC: 回收新生代,因为新生代对象存活时间很短,因此Minor GC 会频繁执行,执行的速度一般也会比较快
  • Full GC: 回收老年代和新生代,老年代对象其存活时间长,因此Full GC 很少执行,执行速度比Minor GC 慢得多

NIO

NIO 主要有三大核心部分:Channel(通道),Buffer(缓冲区), Selector。传统 IO 基于字节流和字符流进行操作,而 NIO 基于 ChannelBuffer(缓冲区)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。Selector(选择区)用于监听多个通道的事件(比如:连接打开,数据到达)。因此,单个线程可以监听多个数据通道。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OAW6MVQD-1597890706282)(C:\Users\晨边\AppData\Roaming\Typora\typora-user-images\image-20200802183955625.png)]

NIO 和传统 IO 之间第一个最大的区别是,IO 是面向流的,NIO 是面向缓冲区的。

Selector

通过轮询的方式去监听多个通道Channel上的事件,从而让一个线程就可用处理多个事件

  • 通过配置监听的通道Channel为非阻塞,那么当Channel上的IO事件还未达到时,就不会进入阻塞状态一直等待,而是继续轮询其他Channel,找到IO事件已经到达的Channel执行。
  • 只有套接字Channel才能配置为非阻塞,而FileChannel不能,为FileChannel配置非阻塞没有意义

img

NIO缓冲区

Java IO没有被缓冲,从流中直接读取。不能前后移动流中的数据。

NIO将数据读取到一个稍后处理的缓冲区,需要时在缓冲区中前后移动。(增加了处理过程的灵活性)

需要检查是否该缓冲区包含所有你需要的数据。确保更多的数据读入缓冲区时,不要覆盖缓冲区里未处理的数据

NIO的非阻塞

IO的各种流是阻塞的。当一个线程调用read()和write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。

NIO使一个线程从某通道发送请求读取数据,如果没有数据就不读取。不保持阻塞状态。一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。

NIO的读取步骤

  • capacity: 最大容量
  • position:当前已经读写的字节数
  • limit:还可以读写的字节数
  1. 新建一个大小为8个字节的缓冲区,此时position为0,而limit = capacity = 8。capacity变量不会改变,下面就讨论会忽略它。

img

  1. 从输入通道中读取5个字节数据写入数据缓冲区,此时position为5,limit保持不变。

img

  1. 在将缓冲区的数据写道输出通道之前,需要先调用flip() 方法,这个方法将limit设置为当前position,并将position设置为0

img

  1. 从缓冲区中取出4个字节道输入缓冲区中,此时position设为4

img

  1. 最后需要调用clear() 方法来清空缓冲区,此时position和limit都被设置为最初位置。

img

JVM类加载机制

JVM 类加载机制分为五个部分:加载,验证,准备,解析,初始化,下面我们就分别来看一下这五个过程。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3w3waDrQ-1597890706292)(C:\Users\晨边\AppData\Roaming\Typora\typora-user-images\image-20200802192858118.png)]

加载

加载是类加载过程中的一个阶段,这个阶段会在内存中生成一个代表这个类的 java.lang.Class 对象,作为方法区这个类的各种数据的入口。注意这里不一定非得要从一个 Class 文件获取,这里既可以从 ZIP 包中读取(比如从 jar 包和 war 包中读取),也可以在运行时计算生成(动态代理),也可以由其它文件生成(比如将 JSP 文件转换成对应的 Class 类)。

验证

这一阶段的主要目的是为了确保 Class 文件的字节流中包含的信息是否符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。

准备

这一阶段的主要目的是为了确保 Class 文件的字节流中包含的信息是否符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。

public static int v = 8080;

实际上变量 v 在准备阶段过后的初始值为 0 而不是 8080,将 v 赋值为 8080 的 put static 指令是程序被编译后,存放于类构造器方法之中。 但是注意如果声明为:

public static final int v = 8080;

在编译阶段会为 v 生成 ConstantValue 属性,在准备阶段虚拟机会根据 ConstantValue 属性将 v赋值为 8080。

解析

解析阶段是指虚拟机将常量池中的符号引用替换为直接引用的过程。符号引用就是 class 文件中的:

  • CONSTANT_Class_info

  • CONSTANT_Field_info

  • CONSTANT_Method_info

等类型的常量。

注意以下几种情况不会执行类初始化

  1. 通过子类引用父类的静态字段,只会触发父类的初始化,而不会触发子类的初始化。
  2. 定义对象数组,不会触发该类的初始化。
  3. 常量在编译期间会存入调用类的常量池中,本质上并没有直接引用定义常量的类,不会触
    发定义常量所在的类。
  4. 通过类名获取 Class 对象,不会触发类的初始化。
  5. 通过 Class.forName 加载指定类时,如果指定参数 initialize 为 false 时,也不会触发类初
    始化,其实这个参数是告诉虚拟机,是否要对类进行初始化。
  6. 通过 ClassLoader 默认的 loadClass 方法,也不会触发初始化动作。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ytoRtXgO-1597890706294)(C:\Users\晨边\AppData\Roaming\Typora\typora-user-images\image-20200802194253675.png)]

在这里插入图片描述

暑期在中国科学院软件应用技术研究所学习的内容。
之前用Typora写了之后上传到GitHub
现在搬运过来csdn了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值