目录

一、类加载器
类装载器ClassLoader 负责装载我们java的字节码文件(.class) 而且class文件有特定的文件标识,类装载器会识别是否是 友善的 class文
Java虚拟机自带的加载器 有 3 + 1
- 加载祖宗的是 Bootstrap(启动类加载器) 最顶层 (打印为null,因为是cpp写的,是祖宗最高级别)
$JAVAHOME/jre/lib/rt.jar(rt:runtime) - 加载java扩展的extension(扩展类加载器)
$JAVAHOME/jre/lib/ext/*.jarjavax(x:extend) - 加载我们写的类 AppClassLoader(应用类加载器)
sum.misc.Launcher 是jvm的入口程序
- 自定义类加载器 继承抽象类ClassLoader
双亲委派机制(parent delegation)就是每一个类加载器收到类加载请求时,他会先去找他的父类加载器,而不是自己先处理,即往上捅,只有父类反馈说没有的时候,子类才会尝试自己加载,从而能够保护恶意代码污染源代码,即沙箱安全。
执行引擎(Execution Engine):视频没有详细说,ppt就一行字:负责解释命令,提交操作系统执行
二、native
java的native interface 本地方法接口 修饰的东西,是不需要java程序员了解,是java调用系统级别的方法,光靠java自己做不到了,不归java管了。需要调用第三方提供的接口(c/操作系统等),比如开启线程
早期c/c++横行的时候,java为了生存必须要调用c/c++程序,所以在内存中专门开辟了一块区域处理native代码,即本地方法栈,在Execution Engine执行时加载本地方法库
不过现在native方法越来越少,java主要是干微服务,异构领域通信发达
三、程序计数器
程序计数器嘛,就相当于是一个指针,用来存储下一条指令的地址,就是PC寄存器
即 指向方法区中的方法字节码,由执行引擎读取
如果执行的是Native方法的话,计数器则为空
四、方法区
方法区是规范,与具体实现无关。
方法区要求放java类的模板(每一个类的结构信息)就是Class文件,堆中的数据类型指向的方向
在不同的虚拟机里有不同的实现,如永久代(PermGen space) 和 元空间(Metaspace),
是线程共享的,而且会发生垃圾回收
栈管运行,堆管存储
HotSpot是java虚拟机的一种实现,是Sun JDK 和 OpenJDK所带的虚拟机
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m79Bsmhm-1623845658156)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/a05b3009-cd06-4295-ab79-eede49d683b7/Untitled.png)]
error级别和exception级别的错误的区别:
error不可处理,由jvm抛出的严重问题,需要直接修改程序
exception可以针对性处理
五、Java栈
存放八种基本数据类型+引用数据类型+实例方法
是线程私有的,一般不发生gc回收,只要线程结束,栈就随之结束
栈帧:栈中数据存在的格式
栈帧中存储3类数据:
- 本地变量(Local Variables):输入输出参数和方法内变量
- 栈操作(Operand Stack):记录入栈出栈操作
- 栈帧数据(Frame Data):包括类文件、方法等
栈满会报StackOverflow错误 ,是error级别
六、堆
1.数据存放
逻辑分三种:
- 新生代
- 老年代
- 永久代/ 元空间(方法区的一种实现)如rt.jar放进永久代
永久代的空间是在虚拟机中,元空间是在物理内存中
物理分两种:
- 新生代
- 老年代
新生代又分 伊甸区→幸存者0区→幸存者1区
伊甸区是GC = YGC
幸存者0区和幸存者1区又可以理解成from to区,gc清空伊甸区和from区,留存的对象去to区并生命周期+1,swap from and to,如果生命周期到了15还是存活,就进入老年代
养老区是 Full GC =FGC
Full GC多次,发现养老区没空间腾出来,就会报OOM错误(Out Of Memory)
内存空间的分配
Java虚拟机默认占本机物理空间的1/4
新生代占1/3堆空间 老年代占2/3堆空间
堆内存调优+日志信息解读
-Xmx 默认物理内存的1/4 -Xms 默认物理内存的1/64 -XX:+PrintGCDetails
我们也可以在idea中手动调参,实践中工程经验必须把初始值和最大值设置成一样,避免内存忽高忽低,jvm和系统争抢内存,产生停顿
2.GC回收算法
- 引用计数法
- 复制算法
- 标记清除法
- 标记整理法
Java的线程分为非守护线程和守护线程,GC是守护线程,只要JVM实例中有非守护线程,守护线程就要全部工作
-
普通GC(minor GC):常常发生在新生代的gc,因为新生代对象大多存活率都不高,所以minor GC经常发生
-
全局GC(major GC or Full GC):发生在老年代的gc,通常伴有一次minorGC。速度一般要比minor GC慢上10倍
a.引用计数法
现在主流的JVM无一使用引用计数方式来实现Java对象的自动内存管理,因为会存在“死锁现象”,本身维护需要消耗,而且较难处理循环引用
class CountGC {
private byte[] bigSize = new byte[2 * 1024 * 1024];//这个成员属性唯一的作用就是占用一点内存
Object instance = null;
public static void main(String[] args) {
CountGC objectA = new CountGC();
CountGC objectB = new CountGC();
objectA.instance = objectB;
objectB.instance = objectA;
objectA = null;
objectB = null;
System.gc(); //手动唤醒gc
}
}
b.复制算法
优点:没有内存碎片,时间效率高
缺点:需要内存空间
主要发生在新生代,MinorGC采用
Eden:From:To = 8:1:1
New:Old=1:2堆空间

c.标记清除法
优点:不需要额外的内存空间
缺点:产生内存碎片、时间效率低

d.标记压缩法(标记整理法)
优点:不需要额外的内存空间,不会产生内存碎片
缺点:时间效率也不高

老年代GC:标记清除+标记压缩混合实现
多次标记清除+一次压缩 (Mark-Sweep-Compact)
总结
时间效率:复制>标记清除>标记压缩
内存碎片:复制算法=标记压缩>标记清除
内存利用率:标记压缩算法=标记清除算法>复制算法
更优算法:G1垃圾回收
有老哥评论指点:
首先非常感谢up,和视频的周阳老师,然后JVM 那块我补充一点点:
判断对象是否已经死亡的算法:引用计数算法,可达性分析算法;
四个垃圾收集算法:标记清除算法,复制算法,标记整理算法,分代收集算法;
七个垃圾收集器:Serial,SerialOld,ParNew,Parallel Scavenge,Parallel Old,CMS,G1.
视频中把引用技术算法归到垃圾收集算法了,以及没有提到可达性分析算法。还有就是垃圾收集器那块没有讲到。大家可以根据2,4,7这几个数字来差缺补漏。
本文详细介绍了Java虚拟机的内存结构,包括类加载器、native方法、程序计数器、方法区、Java栈、堆内存以及垃圾回收算法。重点阐述了双亲委派机制、本地方法栈、堆内存的分区以及各种GC算法的工作原理。同时,文章提及了堆内存调优和日志信息解读,以及不同GC算法的时间效率和内存碎片情况。

634

被折叠的 条评论
为什么被折叠?



