当前位置:网站首页>从0开始设计JVM ,忘记名词跟上思路一次搞懂
从0开始设计JVM ,忘记名词跟上思路一次搞懂
2022-08-10 23:47:00 【闲猫】
必要性
来回看jvm的东西,再拿起来还是记不住,--XX***参数一大堆,各种组件和名词,根本记不住,调优起来缺不清楚怎么设置这些参数。看一些经验行的比例,在解决问题的时候还是不清楚。
原因是你没有正真搞清楚JVM
怎么解决? 你自己设计一个JVM,过一遍逻辑,把JVM中各部分结构化,有机的组合在一块,存在是为了解决问题,妥协是为了平衡优缺点。
需求
有C,C++语言了,也有局限,对平台依赖严重,空间需要自己管理,很麻烦一不小心就遗忘导致了内存泄露。怎么解决?
怎么实现一次实现到处运行
Jvm:Java visual machine。
抽象出字节码,JVM去根据字节码适配到各个平台就行,上面的操作就不需要考虑平台差异性喽。
基于Jvm语言包括:Java,kotlin,Scala,Clojure,Groovy,Jython,JRuby,Ceylon,Eta,Haxe
加载和执行字节码需要啥
需求:
字节码就是执行的逻辑,加载到内存,需要所有人都可以看的到
静态变量是公用的,放哪呢
根据类创建的对象,这部分得可以共享,否则一个用户干点事所有的基础对象都得自己创建。对象包括放对象引用的地址信息,对象数据空间。
每个用户都有自己的隐私,也需要给每个用户独占的一块空间
最后是控制代码执行的指针,即:现在代码执行到那了,也需要保存下来。对单核机器来说,当前需要执行的指针只有一个,如果要挂起就保存起来,用当前执行的用户的指针就行
上面都是静态的空间,下来说动的部分:
首先需要加载字节码,其次是需要根据字节码执行代码,分配空间,创建线程,按照方法调用逻辑执行,执行的过程中对没用的垃圾对象要及时回收
组件架构图:
蓝色:线程独占
橙色:共享
加载过程
有一个类来将class文件加载到内存中,放在方法区域中
安全检查这个类,确保加载的类符合 JVM 规范和安全
如果没问题,就给静态变量分配空间,并初始化,这类数据是类唯一的,可以也放在方法区内。
如果static String a=”abc”,需要将”abc”放在常量池中,并将a赋值常量池值”abc”的引用。
这时这个类就就绪了。
卸载,GC将字节码删除的过程
一个和一批加载自然不同,比如:java本身提供一些类,如果你的类和java的类重名了咋办,如果没有啥规则就乱套了。
再一个,如果java的字节码,你自己写个类加载器,那加载到内存中谁知道出现啥问题,你也不清楚人家底层呀,更模板模式一样,即使允许你自己写类加载器,也只给你预留部分你自己可以实现的部分才可以。
重名咋办:那优先用java提供的
类加载器:java提供的类由Java自己的类加载器加载;自己写的类,java给个默认的加载器,也可以自定义一个加载器,但必须是继承人家的ClassLoader类,只有部分方法可以自定义。
垃圾对象回收
垃圾回收:1. 哪些算是垃圾;2. 怎么回收。
垃圾认定:程序没法找到这个对象,自然就没用了,表现为这个对象的引用是否被别人保留,没有引用也就没法找到该对象了。或者给每个对象搞一个计数器,谁用记录,最后计数器为0就没人用了。
怎么回收:
- 标记清除:检查可到达时标记;回收的时候将数据擦除进行回收。一大块空间用过一遍就会很多碎片产生,但简单基本不动
- 标记复制:分配的时候挨个分配空间,检查可到达并标记垃圾对象,再拿出一块空间将有用的对象赋值过去。需要赋值就需要一半的空间冗余存储,不能分配,否则就没法复制了。每次都得复制全部对象,即使啥没动也得复制一遍。
- 标记压缩:同样,刚开始标记好,内存是一条线,将有用对象从0x0开始码齐。最坏的情况是复制全部。
实际逻辑:标记操作同时进行,从根(GCRoot)开始,找有用的对象,找到就改复制复制,该压缩压缩。
结合实际设计:任何一种压缩算法都不是最优的,最好情况是集成所有算法的有点。那就需要对对象分类了,一个进程启动后启动类对象基本不变,方法中局部变量用完就得回收,线程之间共享的对象就不确定啥时候该回收了。很容易分为:确定永久不变的;王八级;朝生夕死级;不确定的生存时间的。 还有一个维度就是大小,大对象很很好性能,尽可能不动,动的时候尽可能就是回收(只复制活对象,垃圾是不动的)。
除了从语法上就可以看出永久不变的,直接放在一个区域。
其他其实都是存活时间[一次gc,无穷大),可以gc抽象为过年,每一次gc没有死掉的,对象长一岁。那这个节点怎么确定,基于统计数据吧。
- 1岁以下,即第一次gc就回收的
- 老年定义,15岁(默认)
- 1-15岁对象
详细逻辑分析:
- 婴儿区Eden-中间年龄:标记复制算法,空间不够用了,就GC将有用的对象符合到中间区域
- 中间区域Survivor:按照没有分为两个算,现在Eden要复制一批到Survivor区域,如果空间够就在后边分配就行,如果不够Survivor也需要进行压缩下,再在后边接着分配。那这次Survivor算不算增加一岁呢。那如果Survivor即使压缩空间后空间也不够呢,怎么复制过去。
- 如果分为两个就好多了:A-(Eden + From)作为复制起始,复制有用的对象到To空间,From中超过年龄的直接复制到Old中,如果Eden + From)>To,为了可用将稍微老一点的对象放在Old中。复制完,交换From和To,继续在Eden空间不够用的时候触发Gc功能。
- Old空间呢:只有当From复制超过15岁的对象没有空间给分配了,才进行GC。Old中放有长时间存在,或者大对象。如果也来个复制算法,太浪费空间了。如果直接进行压缩,浪费性能,想想中间有一个大对象没用了,还有必要把后面的压缩起来吗?直接分配就可以了,所以用标记清理算法。当然会产生碎片,当所有碎片,包括后面也没整块的空间了,那就需要来一次压缩了,没法子必须保证可用吧。
持久区域就没必要花了,反正也不GC
两个问题:
- 各个区域大小怎么划分
- 多少岁还是老年了
直接给答案吧:
- Eden:From:To = 8:1:1
- >=15 就算Old了
我个人意见是统计出来的,有人说15是因为存储空间原因,我不以为然,如果统计数据是32岁最合适,我就不信JVM不会设计一个超过4b的存储空间来存储这个值。
参考
边栏推荐
- Doris建表注意事项,实时数仓的同学记得收藏
- 线程相关知识点
- Cache knowledge summary
- Microsoft: Into Focus with Scott Guthrie Scott Hanselman Rajesh Jha and Kevin Scott | KEY11
- 6.0深入理解MySQL事务隔离级别与锁机制
- Is there a way out in the testing industry if it is purely business testing?
- 开源一夏|OpenHarmony如何选择图片在Image组件上显示(eTS)
- Multilingual Translation - Multilingual Translation Software Free
- 16. 文件上传
- CSDN21天学习挑战赛之折半查找
猜你喜欢
SQL注入基础
给肯德基打工的调料商,年赚两亿
2. 依赖管理和自动配置
There is no recycle bin for deleted files on the computer desktop, what should I do if the deleted files on the desktop cannot be found in the recycle bin?
13. Content Negotiation
11. 自定义转换器
鲲鹏编译调试及原生开发工具基础知识
逮到一个阿里 10 年老 测试开发,聊过之后收益良多...
How to recover data from accidentally deleted U disk, how to recover deleted data from U disk
[Excel知识技能] 将文本型数字转换为数值格式
随机推荐
15. 拦截器-HandlerInterceptor
Is there a way out in the testing industry if it is purely business testing?
15. Interceptor - HandlerInterceptor
6.0深入理解MySQL事务隔离级别与锁机制
一条SQL查询语句是如何执行的?
jsp中使用JDBC连接mysql的方法与实例
Kioptrix Level 1 靶机wp
学习Apache ShardingSphere解析器源码(一)
基于SSM实现手机销售商城系统
如何快速把握行业机会,更高效地推陈出新,是一个重要的命题
C语言篇,操作符之 移位运算符(>>、<<)详解
高性能MySQL核心整理强势来袭
CF1427F-Boring Card Game【贪心】
web 性能提升(将持续更新……)
有哪些可以投稿软件工程/系统软件/程序设计语言类外文期刊、会议?
ROS Experimental Notes - Install QPEP and Intel-MKL
CSDN21天学习挑战赛之折半插入排序
《剑指offer》题解——week2(持续更新)
盘点美军的无人机家底
特殊类与类型转换