免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 126558 | 回复: 0
打印 上一主题 下一主题

【话题讨论+送书福利】如何成为Java大神级程序员?虚拟机的知识你掌握了吗? [复制链接]

论坛徽章:
8
2017金鸡报晓
日期:2017-01-10 15:13:2915-16赛季CBA联赛之天津
日期:2019-06-20 14:25:4015-16赛季CBA联赛之天津
日期:2019-08-20 23:06:5319周年集字徽章-庆
日期:2019-08-27 13:24:4219周年集字徽章-19
日期:2019-09-06 18:55:5019周年集字徽章-年
日期:2019-09-06 18:55:5019周年集字徽章-周
日期:2019-09-20 17:18:2220周年集字徽章-CU
日期:2020-11-11 13:06:03
1 [报告]
发表于 2021-09-19 16:45 |显示全部楼层
(1)请详细说说你所知道的Java类的生命周期。
Java类的生命周期类从被加载到虚拟机内存开始,到卸载出内存为止,整个生命周期包括:加载、验证、准备、解析、初始化、使用和卸载七个阶段。 其中加载、验证、准备、初始化、和卸载这5个阶段的顺序是确定的。

(2)Java是如何对运行时数据区进行划分的?为什么要这么划分?
运行时数据区主要分为以下几个部分:
·方法区
·虚拟机栈
·本地方法栈
·堆
·程序计数器
其中,按照线程在各个区域的数据是否共享划分为:
·线程共享部分:方法区、Java 堆以及运行时常量池(归属于方法区)
·线程私有部分:虚拟机栈、本地方法栈、程序计数器

JVM规范规定,JVM 在执行 Java 程序的过程中会把它所管理的内存划分为若干个不同的数据区域。这些区域都有各自的用途。以及创建和销毁的时间,有的区域随着虚拟机进程的启动就存在了,而有些区域则依赖用户线程的启动和结束而建立和销毁。JVM 规范对 JVM 定义了运行时统一的内存划分规范,统一了标准,类似于 JDBC 规范一样。其内存区域划分规范对于 JVM 的含义类似于我们 Java 中的接口,都是起到了规范的作用,JVM 是一台可以运行 Java 应用程序的抽象的计算机。

(3)详细说一下虚拟机为Java对象分配内存的过程,越详细越好。
虚拟机遇到一条new指令时,首先检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并检查这个符号引用代表的类是否已经被加载、解析和初始化过。如果没有,先执行相应的类加载过程。虚拟机为新生对象分配内存。对象所需内存大小在类加载完成后就可以确定,为对象分配内存等同于把一块确定大小的内存从Java堆中划分出来。
内存分配的方式有两种:① 指针碰撞: java堆如果规整,一边是用过的内存,一边是空闲的内存,中间一个指针作为边界指示器; 分配内存只需向空闲那边移动指针空出与对象大小相等的空间;
② 空闲列表: 如果不规整,即用过的和空闲的内存相互交错;则虚拟机需要维护一个列表,记录哪些内存可用;分配内存时查表找到一个足够大的内存,并更新列表记录。
选择哪种分配方式是根据这个虚拟机所采用的垃圾收集器是否带有压缩整理功能决定的:如果虚拟机的虚拟器带压缩整理功能,则系统采用指针碰撞的内存分配算法;否则采用空闲列表的算法。
并发时,上面两种方式分配内存的操作都不是线程安全的,有两种解决方案:
① 同步处理
JVM采用CAS(Compare and Swap)机制加上失败重试的方式,保证更新操作的原子性;
CAS:有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做;
② 本地线程分配缓冲区(TLAB)
把分配内存的动作按照线程划分在不同的空间中进行:每个线程在Java堆预先分配一小块内存,称为本地线程分配缓冲区(Thread Local Allocation Buffer,TLAB);哪个线程需要分配内存就从哪个线程的TLAB上分配;只有TLAB用完需要分配新的TLAB时,才需要同步处理。
JVM通过"-XX:+/-UseTLAB"指定是否使用TLAB。
内存分配完之后,虚拟机需要将分配到的内存空间都初始化为零值。如果用TLAB,则在TLAB分配时进行。这保证了程序中对象(及实例变量)不显式初始赋零值,程序也能访问到零值。
虚拟机对对象进行必要的设置,例如这个对象是哪个类的实例、 如何才能找到类的元数据信息、 对象的哈希码、 对象的GC分代年龄等信息。这些信息存放在对象的对象头(Object Header)之中。
执行init方法,即按照程序员的意愿进行初始化。至此真正可用的对象才算完全被构造出来。

(4)假设年轻代采用复制算法,老年代采用“标记-整理”算法管理虚拟机堆时,当线上发生YGC过于频繁、YGC的STW过长、FGC过于频繁、FGC的STW过长这几种情况时,请列举每个现象发生的可能原因,要有理有据。例如,YGC频繁,那么可能是年轻代采用了单线程的复制算法导致内存回收效率低下,而内存的分配速度大时就造成了年轻代的内存吃紧,又或者年轻代分配的内存过小而又不能动态扩容的情况下导致频繁YGC。
导致FGC问题的可能原因包括:
1、大对象:系统一次性加载了过多数据到内存中(比如SQL查询未做分页),导致大对象进入了老年代。
2、内存泄漏:频繁创建了大量对象,但是无法被回收(比如IO对象使用完后未调用close方法释放资源),先引发FGC,最后导致OOM.
3、程序频繁生成一些长生命周期的对象,当这些对象的存活年龄超过分代年龄时便会进入老年代,最后引发FGC. (即本文中的案例)
4、程序BUG导致动态生成了很多新类,使得 Metaspace 不断被占用,先引发FGC,最后导致OOM.
5、代码中显式调用了gc方法,包括自己的代码甚至框架中的代码。
6、JVM参数设置问题:包括总内存大小、新生代和老年代的大小、Eden区和S区的大小、元空间大小、垃圾回收算法等等。

导致YGC问题的可能原因包括:
1、对存活对象标注时间过长:比如重载了Object类的Finalize方法,导致标注Final Reference耗时过长;或者String.intern方法使用不当,导致YGC扫描StringTable时间过长。
2、长周期对象积累过多:比如本地缓存使用不当,积累了太多存活对象;或者锁竞争严重导致线程阻塞,局部变量的生命周期变长。
YGC问题其实比较难排查。相比FGC或者OOM,YGC的日志很简单,只知道新生代内存的变化和耗时,同时dump出来的堆内存必须要仔细排查才行。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP