JVM探究狂神学习笔记

JVM探究狂神

  • 请你谈谈你对JVM的理解 ? java8虚拟机和之前的变化更新 ?
  • 什么是OOM, 什么是栈溢出StackOverFlowError? 怎么分析 ?
  • JVM的常用调优参数有哪些 ?
  • 内存快照如何抓取,怎么分析Dump文件 ? 知道吗 ?
  • 谈谈JVM中,类加载器你的认识 ?

1. JVM的位置

在这里插入图片描述

2. JVM的体系结构

最开始的时候是一个.java文件,之后会通过javac命令转换成对应的.class字节码文件。java虚拟机通过类加载器ClassLoader从硬盘中找到对应的.class文件,并装载然后进入运行时数据区(Runtime Data Area),其中运行时数据区包括方法区(Method Area), java栈(Stack), 本地方法栈(Native Method Stack) ,堆(Heap),程序计数器。
其中本地方法栈要调用本地方法,所以肯定是有本地的方法接口。其中本地方法接口是和本地库相连的
还有一个执行引擎
垃圾回收的地方在堆中,栈中不能有垃圾,一旦有垃圾,程序直接就崩了
所谓的JVM调优是在堆(99%)和方法区
针对于lombok插件来说,运行的时候动态的生成getter和setter方法,它是在执行引擎这里动了

在这里插入图片描述

3. 类加载器

在这里插入图片描述

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

4. 双亲委派机制

在这里插入图片描述

* 为什么要引入双亲委派机制?

写了一个项目,这个项目里面的String都是公用的,现在自定义了一个string,通过网络传到项目中,一运行整个项目就挂掉了。
为了一个项目的安全性,引入双亲委派机制
java虚拟机对class文件采用的是按需加载的方式,也就是需要使用该类的时候,才会将这个类对应的class文件加载到内存中生成对应class对象,而且加载某个class类的时候,采用的是双亲委派机制,即把请求委派给父类,交给父类处理,
静态代码块是在初始化阶段调用的
优势:1 避免类的重复加载 2 保护程序的安全,防止核心的API被随意篡改

* 双亲委派模式

1 如果一个类加载器收到了类加载的请求,它并不会自己先去加载,而是把这个请求委派给父类加载器。
2 如果父类加载器还有父类的加载器的话,还会继续委派,直到顶层的根(启动类)加载器
3 如果启动类加载器可以完成加载,就直接返回,如果不能加载,子加载器才会尝试着去加载。这就是双亲委派模式

5. 沙箱安全机制

自定义一个String类,但是在加载自定义的String类的时候,会率先使用根加载器加载而根加载器在加载的过程中会先加载jdk自带的文件
(rt.jar包中java\lang\String.class),报错信息说没有Main方法,就是因为加载的是rt.jar包下面的String类,这样可以保证java核心源代码的安全,这就是沙箱安全机制

6. Native

JNI:java的本地方法接口(java Native Interface)

说明凡是带了Native关键字的,说明了Java的作用范围达不到了,会去调用底层c语言的库
此时会进入本地方法栈,调用本地方法接口(JNI)
本地方法栈通过本地方法接口就是调用本地方法库
JNI的作用就是扩展java的使用,融合不同的语言为java所用! C, C++
java诞生的时候,C,C++横行,想要立足,就必须要有调用C,C++的程序
在内存区域中专门开辟了一块标记的区域,Native Method Stack ,登记Native方法
在最终执行的时候,加载本地方法库中的方法通过JNI
private native void start0();

7. PC寄存器

程序计数器: Program Counter Register
每一个线程都有一个程序计数器,是线程私有的,就是一个指针,指向方法区中的方法字节码(用来存储指向:像一条指令的地址,也是即将要执行的指令代码),在执行引擎读取下一条指令,是一个非常小的内存空间,几乎可以忽略不计。

8. 方法区

MethodArea:方法区
方法区是被所有的线程共享,所有字段和方法字节码,以及一些特殊方法,如构造函数,接口代码也在此定义,所有定义方法的信息
都存在该区域,此区域属于共享区间

静态变量, 常量,类信息(构造方法,接口定义),运行时的常量池存在方法区中,但是实例变量存在堆内存中,和方法区无关
带有static,final,Class模板,运行时的常量池

9. 栈

1 栈:数据结构
程序 = 数据结构+算法 :持续学习~
程序 = 框架+业务逻辑~ :淘汰! SSM+SpringBoot+SpringCloud

栈:后进先出
队列:先进先出(FIFO first Input first Output)

为什么main()先执行,后结束
栈:栈内存,主管程序的运行,声明周期和线程同步;
线程结束,栈内存也就释放了,对于栈来说,不存在垃圾回收,一旦线程结束了,栈也就结束了
栈:8大基本类型+引用数据类型+实例的方法
栈运行的原理:它是以栈帧的形式存在

栈运行的原理: 栈帧
栈满了 : StackOverflowError

栈+堆+方法区 :
在这里插入图片描述

栈里面存了什么东西?
怎么存?

10. 三种JVM

Sun公司 Java HotSpot™ 64-Bit Server VM (build 25.101-b13, mixed mode)
BEA JRockit 是世界上最快的JVM
JIT编译器 是Java运行时环境的一部分
我们学习都是:HotSpot

11. 堆

Heap 一个JVM只有一个堆内存,堆内存的大小是可以调节的
类加载器读取了类文件后,一般会把什么东西放到堆中? 类, 方法,常量, 变量 ~,保存我们引用类型
堆内存中还要细分为三个区域:
新生区(伊甸园区)Eden Space 新生区又分为伊甸园区 幸存区0区 幸村区1区
养老区 ()
永久区 ()
垃圾回收分为轻量级垃圾回收,重量级垃圾回收
GC垃圾回收主要是在伊甸园区和养老区
假设内存满了,OOM, 堆内存不够! (这个表明堆内存满了,JVM爆了)
Exception in thread “main” java.lang.OutOfMemoryError: Java heap space
在JDK8以后,永久存储区改了名字叫做(元空间)
在这里插入图片描述

11.1 新生区、老年区

新生区:是一个类的成长,诞生,甚至死亡;
新生区:伊甸园区:所有的对象生成是在伊甸园区
幸存者区(幸存者0区,幸存者1区)
举例:例如在伊甸园区只能存储10个对象,当存满的时候,会触发一次轻GC。有的对象可能还存在引用,那么就继续保存,有些引用
就清掉了,那么还在引用的对象就从伊甸园区转到了幸存0区,如果经历几次GC还有引用,就会触发重GC,那么就会把伊甸园区,幸存者区的对象全部再清理一次,如果还满就OOM
真理:经过研究,99%都是临时对象!

11.2 永久区

这个区域常驻内存的。用来存放JDK自身携带的Class对象。Interface元数据,存储的是Java运行时的一些环境或类信息,这个
区域不存在垃圾回收!关闭虚拟机就会释放这个区域的内存
一个启动类加载了大量的第三方的jar包,Tomcat部署了太多的应用,大量的动态生成的反射类。不断的被加载,直到内存满,就会出现OOM
jdk1.6 :方法区位于永久代,永久代和区相互隔离,永久代的大小在启动JVM的时候,可以设置一个固定值,大小不可变
jdk1.7 : 永久代,但是慢慢退化了,去永久代,常量池在堆中
jdk1.8 : 无永久代,方法,常量池在元空间,元空间仍然与堆不相连,但与堆共享物理内存,逻辑上可认为在堆中。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sIfWWFpy-1668407841802)(vx_images/299283523236730.png)]

11.3 堆内存调优

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
在这里插入图片描述

-Xms1024m -Xmx1024m -XX:+PrintGCDetails

-Xms: 最小堆的大小
-Xmx: 最大堆的大小
-Xmn:年轻代的大小

echo: 输出
示例代码:

public class Demo02 {
    public static void main(String[] args) {
        //返回虚拟机试图使用的最大内存
        long max = Runtime.getRuntime().maxMemory(); //1024*1024=1M
        //返回jVM的初始化总内存
        long total = Runtime.getRuntime().totalMemory();

        System.out.println("max="+max+"字节\t"+(max/(double)1024/1024)+"MB");
        System.out.println("total="+total+"字节\t"+(total/(double)1024/1024)+"MB");
    }
}

运行结果:

max=1029177344字节	981.5MB
total=1029177344字节	981.5MB
Heap
 PSYoungGen      total 305664K, used 20971K [0x00000000eab00000, 0x0000000100000000, 0x0000000100000000)
  eden space 262144K, 8% used [0x00000000eab00000,0x00000000ebf7afb8,0x00000000fab00000)
  from space 43520K, 0% used [0x00000000fd580000,0x00000000fd580000,0x0000000100000000)
  to   space 43520K, 0% used [0x00000000fab00000,0x00000000fab00000,0x00000000fd580000)
 ParOldGen       total 699392K, used 0K [0x00000000c0000000, 0x00000000eab00000, 0x00000000eab00000)
  object space 699392K, 0% used [0x00000000c0000000,0x00000000c0000000,0x00000000eab00000)
 Metaspace       used 3272K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 358K, capacity 388K, committed 512K, reserved 1048576K

Process finished with exit code 0

    //PSYoungGen      total 305664K
    // ParOldGen       total 699392K
    //(305664+699392)/1024 = 981.5MB
    结果:元空间逻辑上存在,物理上不存在

碰见堆内存满了OOM,如何调节?
第一种方法:扩大内存
最快的方法:能够看到代码第几行出错:内存快照分析工具, MAT,Jprofier(作用)
最慢的方法:debug,一行行分析代码!
MAT,Jprofier(作用):
分析Dump内存文件,快速定位内存泄露:
获得堆中的数据
获得大的对象

12. GC

JVM在进行GC时,并不是对这三个区域统一回收,大部分回收都是在新生代

  • 新生代
  • 幸存区(0, 1)(from, to) 幸存者区是可以互相交换的。
  • 老年区
    GC两种类: 轻量级GC(普通GC),重量级GC(全局GC)

题目:
JVM的内存模型和分区~详细到每个区放什么?
堆里面的分区?Eden,from,to,老年区 说说他们的特点
GC的算法有哪些?标记清除法:哪些用过的做标记,没用过的清理掉
标记压缩,复制算法,引用计数法 怎么用的?
轻GC和重GC分别在什么时候发生?
引用计数法:假设一个对象A用了1次就加一个1,假设这个对象A用了2次就加一个2,计数器本身也会有消耗,当对象的数是0的话就清除掉
谁空谁是幸村区to

12.1 复制算法

about:GC复制算法主要用在from跟to区来使用的

  1. 每次GC都会将Eden活的对象移到幸存区中:一旦Eden区被GC后,就会是空的。
  2. 注意:幸存之from区和to区是不断的在改变的
    当一个对象经历15次GC,都还没有死
    参数:-XX:MaxTenuringThreshold = 5 最大存活时间 此参数可以设定进入老年代的时间

每一次垃圾回收之后,伊甸园区是空的,to区是空的
在这里插入图片描述

好处:没有内存的碎片
坏处:浪费了内存空间(to区是空的)。假设对象是100%存活(极端情况下)
复制算法最佳使用场景:对象存活度较低的时候;

12.2 标记压缩

哪些用过的做标记,没用过的清理掉
在这里插入图片描述

缺点:两次扫描,严重浪费时间,会产生内存碎片
优点:不需要额外的空间!

12.2.1 优化:压缩(防止内存碎片的产生)
    缺点:多了扫描的时间成本

13 GC算法总结

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

思考一个问题:难道没有最优算法吗?
答案:没有,没有最好的算法,有最合适的算法—>分代收集算法

年轻代:
存活率低
复制算法
老年代:
区域大: 存活率
标记清除(内存碎片不是太多)+标记压缩混合实现

<深入理解jvm>

14. JMM

java Memory Model
1 什么是JMM?
java内存模型
2 它干嘛的? 官方 博客 对应的视频
作用:缓存一致性的协议,用于定义数据读写的规则(遵守,找到这个规则)
JMM定义了线程工作和主内存之间的抽象关系:线程之间的共享变量存储在主内存中,每个线程都有一个私有的本地内存(Local Memory).
在这里插入图片描述

解决共享对象可见性这个问题: volatile关键字

3 它该如何学习?
JMM:抽象的概念,理论
volatile

15. 总结

  1.百度
  2.思维导图

单点登录~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值