0
点赞
收藏
分享

微信扫一扫

jvm笔记

三千筱夜 2022-04-15 阅读 15
java

什么是JVM?

定义:Java Virtual Machine - java 程序的运行环境(java二进制字节码的运行环境)

好处:

  • 一次编写,到处运行
  • 自动内存管理,垃圾回收功能(和当时的c,c++比较)
  • 数组下标越界检查(避免改变其他数据)
  • 多态(区方法表机制)

相关概念:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JZudu324-1649992893009)(jvm笔记.assets/image-20220407220837142.png)]

学习jvm好处

常见jvm

jvm结构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dkRpGi7Z-1649992893010)(jvm笔记.assets/image-20220407222045339.png)]

内存结构

1.程序计数器

2.虚拟机栈

3.本地方法栈

4.堆

5.方法区

P5程序计数器:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Iy2YEOol-1649992893010)(jvm笔记.assets/image-20220407222653586.png)]

1.1定义:Program Counter Register 程序计数器(寄存器)

  • 作用,是记住下一条jvm指令的执行地址
  • 特点
    • 是线程私有的
    • 不会存在内存溢出

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iMS15SCy-1649992893011)(jvm笔记.assets/image-20220408105600204.png)]

p7栈

栈-线程运行需要的内存空间

栈帧:每个方法运行时需要的内存(参数,局部变量,返回地址),对应一次方法的调用,

p8栈的演示

Java Virtual Machine Stacks(Java 虚拟机栈)

  • 每个线程运行时所需要的内存,成为虚拟机栈
  • 每个栈由多个栈帧(Frame)组成,对应着每次方法调用时所占用的内存
  • 每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法

p9栈-问题辨析

1.垃圾回收是否涉及栈内存?

每次方法调用后,都会自动弹出栈帧,所以垃圾回收不会涉及栈内存

2.栈内存的分配越大越好吗?

栈内存数越大,线程数越小,只能增加方法递归调用数,而不能增加效率,使用系统默认的栈内存就可以了

3.方法内的局部变量是否是线程安全?

  • 如果方法内局部变量没有逃离方法的作用范围,他是线程安全的
  • 如果是局部变量引用了对象,并逃离方法的作用范围,需要考虑线程安全

p12栈内存溢出

栈帧过多导致栈内存溢出

栈帧过大导致栈内存溢出

p13 栈内存溢出

json循环引用导致内存溢出

@JsonIgnore

p14线程诊断

top命令查询进程对cpu的占用

top
ps H -eo pid,tid,%cpu
ps H -eo pid,tid,%cpu | grep 32655 //过滤线程
jstack 32655 //列出进程内所有的java线程
//把32655转换成16进制找到线程

p15 jstack查询死锁

p16本地方法栈

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PPs4Bc5k-1649992893011)(jvm笔记.assets/image-20220409101808007.png)]

调用本地代码时提供的栈

p17堆

之前的栈或者本地方法栈都是线程私有的,堆可以看成线程共享的

Heap 堆

  • 通过new关键字,创建对象都会使用堆内存

特点

  • 它是线程共享的,堆中对象都需要考虑线程安全的问题

  • 有垃圾回收机制

p18 堆内存溢出

方法 一直没有被回收导致的

p19堆内存诊断

1.jps工具

  • 查看当前系统中有哪些java进程

2.jmap工具

  • 查看堆内存占用情况

    jmap -heap 18756  
    

3.jconsole工具

  • 图像界面的,多功能的监测工具,可以连续监测

22 方法区

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FMbHUTJ0-1649992893011)(jvm笔记.assets/image-20220409114754889.png)]

定义:jvm规范对方法区的定义

存储类的数据

23-24 方法区内存溢出

1.8以前 PermGen 永久代

1.8以后 变成一个 Metaspace 元空间 用于保存,所以不会看到内存溢出

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-G6Unaaam-1649992893012)(jvm笔记.assets/image-20220409131025757.png)]

场景

  • spring
  • mybatis

cglib动态代理

5.4运行时常量池(25-)

  • 常量池 Constant pool,就是一张表,虚拟机指令根据这张常量表找到要执行的类名,方法名,参数类型,字面量等信息

  • 运行时常量池,是*.class文件中的,当该类被加载,它的常量池信息就会放入运行时常量池(在内存中),并把里面的符合地址变为真实地址

5.5 StringTable(27-)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p8Mzxno3-1649992893013)(jvm笔记.assets/image-20220409134423194.png)]

//StringTable["a","b","ab"] hashtable结构,不能扩容 
public class Demo1_22{
    // 常量池中的信息,都会被加载到运行时常量池中,这时 a, b, ab 都是常量池中的符号, 还没有变为java字符串对象
    //ldc #2 会把 a 符号变为 "a" 字符串对象
    //ldc #3 会把 b 符号变为 "b" 字符串对象
    //ldc #4 会把 ab 符号变为 "ab" 字符串对象
	public static void main(String[] args) {
        String s1 = "a";
        String s2 = "b";
        String s3 = "ab";
        
        String s4 = s1 + s2;//new StringBuilder().append("a").append("b").toString() new String("ab")
        
        
        String s5 = "a" + "b";//到常量池里找"ab",javac在编译期间的优化,结构已经在编译期缺点为ab,s4在编译期不能确定,只有遇到没遇到的对象才放入串池
     
        String s6 = s4.intern();
        
        sout( s3 == s4);//false
        sout( s3 == s5);//true
        sout( s3 == s6);//true
    }
}

5.5 StringTable特性(32-)

  • 常量池中的字符串仅是符号,第一次用到时才变为对象
  • 利用串池的机制,避免重复穿甲字符串对象
  • 字符串变量拼接的原理是StringBuilder
  • 字符串常量拼接的原理是编译期优化
  • 可以使用intern方法,主动将传尺中还没有的字符串对象放入串池

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Fvh7RD4A-1649992893013)(jvm笔记.assets/image-20220409162811852.png)]

5.6 StringTable位置

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-C7i9OEFe-1649992893013)(jvm笔记.assets/image-20220409164313159.png)]

增加了垃圾回收的效率

5.7 StringTable 垃圾回收

5.8 StringTable 性能调优(39-)

如果,StringTable过长,则查询时间会很长

  • 调整- XX:StringTableSize=桶个数
  • 考虑将字符串对象是否入池

优点:大量字符串时,可以减少内存占用

6.直接内存(41-)

Direct Memory

  • 常见于NIO操作时,用于数据缓冲区
  • 分配回收成本较高,但读写性能高
  • 不受JVM内存回收管理

java不能直接访问的情况(不使用直接内存):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DK1TxIBu-1649992893013)(jvm笔记.assets/image-20220410104117879.png)]

java调用了本地方法(由操作系统提供),cpu从用户态切换为内核态

java代码可以访问的情况(直接内存):减少了一个缓冲区,提高了效率

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h84XVVZI-1649992893013)(jvm笔记.assets/image-20220410105549688.png)]

6.3分配和回收逻辑

  • 使用了Unsafe对象完成直接内存的分配回收,并且回收需要主动调用freeMemory方法
  • ByteBuffer的实现类内部,使用了Cleaner(虚引用)来检测ByteBuffer对象,一旦ByteBuffer对象被垃圾回收,那么就会由ReferenceHandler线程通过Cleaner的clean方法调用freeMemory来释放直接内存

垃圾回收(48-)

1如何判断对象可以垃圾回收

1.1引用计数法(49)

被引用数量达到0时,没人引用了,被垃圾回收(python用)

弊端:循环引用,各自的引用计数都是1,还不能被回收

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zeoPQVwn-1649992893014)(jvm笔记.assets/image-20220410112856320.png)]

1.2可达分析算法(50-)

根对象:不能被当成垃圾回收的对象

如果一个对象被根对象直接简接引用,不能被当做垃圾;如果一个对象没有被根对象直接简接引用,可以被当做垃圾

  • Java 虚拟机中的垃圾回收器采用可达性分析来探索所有存活的对象
  • 扫描堆中的对象,看是否能够沿用Gc Root对象 为起点的引用链找到该对象,找不到,表示可以引用,找不到,表示可以回收
  • 哪些对象可以作为GC Root?

1.3四种引用

1.强引用 没有GC Roots 直接间接引用,可以被垃圾回收

Object object = new Object();
String str = "StrongReference";

2.软引用

  • 仅有软引用引用该对象时,在垃圾回收后,内存仍不足时会再次出发垃圾回收,回收软引用对象
  • 可以配合引用队列来释放软引用自身

3.弱引用 没有被直接的强引用 ,引用,可以被垃圾回收

  • 仅有弱引用引用该对象时,在垃圾回收时,无论内存是否充足,都会回收弱引用对象
  • 可以配合引用队列来释放弱引用自身

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xOPy3hMF-1649992893015)(jvm笔记.assets/image-20220410130316099.png)]

4.虚引用Cleaner

配合ByteBuffer,当ByteBuffer被回收时,进入引用队列,调用Unsafe.freeMemory 释放直接内存

5.终结器引用

当没有对象引用终结器finalize()时,就会创建一个终结器引用。

将终结器引用放入终结器引用,finalizeHandler查看引用队列,发现终结器引用,调用finalize(),之后就可以被回收了。处理优先级较低,不推荐使用这个方法处理资源

2垃圾回收算法(57-)

2.1标记、清除

先标记,把地址放入空闲地址列表,下次分配的时候,去空闲地址列表找。速度快

缺点:容易产生内存碎片

2.2标记、整理

优点:避免过多碎片

缺点:速度慢

2.3复制

把From区域存活的对象,复制到TO区域,清理掉From区域,交换From和To区域

缺点:占用双倍内存空间

3分代垃圾回收(61-)

理解:老年代:家里存放的没有的物品

​ 新生代:楼下垃圾桶里的垃圾

清理流程:

  • 对象首先分配在伊甸园区域
  • 新生代空间不足时,触发 minor gc,伊甸园和from存活的对象使用copy复制到to中,存活的对象年龄加1并且交换from 、to
  • minor gc 会引发 stop the world, 暂停其他用户的线程,等垃圾回收结束,用户线程才恢复运行
  • 当对象寿命超过阈值时,会晋升至老年代,最大寿命是15(4bit)
  • 当老年代空间不足,会先触发 minor gc,如果之后仍空间不足,那么触发 full gc,STW的时间更长

相关虚拟机参数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0qqc0pHk-1649992893015)(jvm笔记.assets/image-20220410154550145.png)]

大对象晋升老年代,不会触发垃圾回收

4垃圾回收器

1.串行

  • 单线程
  • 堆内存较小,适合个人电脑

2.吞吐量优先

  • 多线程
  • 堆线程,多核cpu
  • 单位时间内,STW的时间最短 0.2 0.2 =0.4

3.响应时间优先

  • 多线程
  • 堆线程,多核cpu
  • 尽可能让单次STW的时间最短 0.1 * 5 = 0.5

4.1吞吐量优先

4.2响应时间优先

4.4G1(72-)

略过

5垃圾回收调优

类加载与字节码(96-)

1.类加载概述

2字节码指令

2.4分析a++(112-)

取决于先执行 iload 还是 iinc

iload 将数存入栈,iinc 将数加减

2.5条件判断指令

全在讲指令,这些指令看的有啥用?

2.11异常处理

finally 将里面的代码进行拷贝,分别放到try ,catch的最后,所以一定会执行

面试题

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MGoYxq2l-1649992893015)(jvm笔记.assets/image-20220411123619935.png)]

不建议在finally里写return,因为会吞掉ireturn的操作,

3语法糖(130-)

所谓语法糖,其实就是之java编译器 把*.java 源码编译为 *.class字节码的过程中,自动生成和转换的一些代码,主要是为了减轻程序员的负担,算是java编译器给我们的一个额外复利(给糖吃)

3.1默认构造器

public class Candy1 {
}

编译成class后的代码:

public class Candy1{
    // 这个无参构造器是编译器帮助我们加上的
    public Candy1(){
        super();// 即调用父类的 Object 的无参构造方法,即调用java/lang/Object."<init>":()V
    }
}

3.2 自动拆装箱

public class Candy2{
	public static void main(String[] args) {
        Integer x = 1;
        int y = x;
    }
}

jdk 1.5之前的做法:

public class Candy2{
    public static void main(String[] args) {
        Integer x = Integer.valueOf(1);
        int y = x.intValue();
    }
}

3.3 泛型集合取值

java在编译泛型代码后会执行 泛型擦除 操作,即泛型信息在编译为字节码之后就丢失了,实际类型都当做Object类型来处理:

public class Candy3{
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(10);// 实际调用的是 List.add(Object e)
        Integer x = list.get(0);// 实际调用的是 Object obj = List.get(int index)
    }
}

3.4可变参数

3.5foreach循环

int[] array = {1,2,3,4,5};//优化
for (int e : array) { // 优化
    sout(e);
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sJRqDdyz-1649992893016)(jvm笔记.assets/image-20220411160340407.png)]

4类加载

4.1加载

  • 将类的字节码载入方法区中,内部采用c++的instanceKlass描述java类,它的重要field有:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bA3QrG4j-1649992893016)(jvm笔记.assets/image-20220412104518119.png)]

  • 如果这个类还有父类没有加载,先加载父类

  • 加载和链接可能是交替运行的

4.2链接

  • 验证:验证类是否符合JVM规范,安全性检查

  • 准备:为static变量分配空间,设置默认值

    解析

  • 解析:将常量池中的符号引用解析为直接引用

4.3初始化

发生的时机

类初始化是 懒惰的

  • main方法所在的类,总是会被首先初始化
  • 首次访问这个类的静态变量或静态方法时
  • 子类初始化,如果父类还没初始化,会引发
  • 子类访问父类的静态变量,只会触发父类的初始化
  • Class.forName
  • new会导致初始化

不会导致初始化的情况

  • 访问类的static final静态变量(基本类型和字符串)不会触发初始化
  • 类对象.class不会触发初始化
  • 创建该类的数组不会触发初始化
  • 类加载器的loadClass方法
  • Class.forName 的参数2 为false时

5类加载器

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XsI8EkoU-1649992893016)(jvm笔记.assets/image-20220412144028649.png)]

不同的类加载器,加载不同层级的类

有层级关系,由下到上询问,由上到下加载

5.1启动类加载器

5.2扩展类加载器

5.3双亲委派模式

所谓的双亲委派,就是指调用类加载器的loadClass方法时,查找类的规则

这里的双亲,翻译为上级似乎更合适,因为它们没有继承关系

5.4线程上下文类加载器

SPI,根据接口得到实现类,解耦

5.5自定义类加载器

1)想加载非classpath随意路径中的类文件

2)都是通过接口来实现,希望解耦时,常用在框架设计

3)这些类希望予以隔离,不同应用的同名类都可以加载,不冲突,常见于tomcat容器

步骤:

1.继承ClassLoader父类

2.要遵从双亲委派机制,重写findClass方法

3.读取类文件的字节码

4.调用父类的deineClass方法来加载类

5.使用者调用该类加载器的loadClass方法

6 运行期优化

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ujqRKHbt-1649992893016)(jvm笔记.assets/image-20220412153336627.png)]

热点代码(频繁执行的)编译,

其他解释。

逃逸分析

不会用到的对象不会被创建。c2编译

6.1方法内联

常量折叠

如果发现热点代码,并且长度不太长时,会进行内联,所谓的内联就是把方法内代码拷贝、粘贴到调用者的位置

字段优化

反射优化

内存模型 Java Memory Model

JMM定义了一套在多线程读写共享数据时,对数据的可见性、有序性和原子性的规则和保障

1.1原子性(167-)

原子性:synchronized关键字

1.3解决方法

执行原子操作

2可见性

2.2解决办法

volatile (易变关键字)

它可以用来修饰成员变量和静态成员变量,他可以避免线程从自己的工作缓存中查找变量的值,必须到主存中获取它的值,线程操作volatile变量都是直接操作主存

适用于一个写,多个读,保证可见性,不报证原子性

2.3可见性

举报

相关推荐

JVM笔记

JVM笔记----JVM内存模型

JVM学习笔记

学习笔记——JVM

JVM 学习笔记

jvm学习笔记

0 条评论