当前位置: 首页 > news >正文

jvm冷门知识十讲

一、类加载时出现死锁,jstack无法检测出来

正常代码出现死锁时,可以jstack指令排查出来

public static void main(String[] args) {new Thread(() -> {synchronized (a) {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}synchronized (b) {System.out.println("t1");}}}).start();synchronized (b) {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}synchronized (a) {System.out.println("t2");}}}

Found one Java-level deadlock:
=============================
"Thread-0":
waiting to lock monitor 0x000001db7c081ca8 (object 0x0000000717189d40, a java.lang.Object),
which is held by "main"
"main":
waiting to lock monitor 0x000001db7c080808 (object 0x0000000717189d30, a java.lang.Object),
which is held by "Thread-0"

Java stack information for the threads listed above:
===================================================
"Thread-0":
at com.Test1.lambda$main$0(Test1.java:17)
- waiting to lock <0x0000000717189d40> (a java.lang.Object)
- locked <0x0000000717189d30> (a java.lang.Object)
at com.Test1$$Lambda$1/1324119927.run(Unknown Source)
at java.lang.Thread.run(Thread.java:750)
"main":
at com.Test1.main(Test1.java:29)
- waiting to lock <0x0000000717189d30> (a java.lang.Object)
- locked <0x0000000717189d40> (a java.lang.Object)

Found 1 deadlock.

 但如果在类加载时出现的死锁,jstack无法检测

public class A {static {try {Thread.sleep(1000);// 尝试加载 ClassBClass.forName("com.B");System.out.println("ClassA 静态代码块执行");} catch (ClassNotFoundException e) {e.printStackTrace();} catch (InterruptedException e) {throw new RuntimeException(e);}}
}public class B {static {try {Thread.sleep(1000);// 尝试加载 ClassAClass.forName("com.A");System.out.println("ClassB 静态代码块执行");} catch (ClassNotFoundException e) {e.printStackTrace();} catch (InterruptedException e) {throw new RuntimeException(e);}}
}public static void main(String[] args) {new Thread(() -> {try {Class.forName("com.A");} catch (ClassNotFoundException e) {e.printStackTrace();}}).start();try {Class.forName("com.B");} catch (ClassNotFoundException e) {e.printStackTrace();}System.out.println("main");}
}

总结:不要在类初始化做有锁操作 

二、类的对象保存在堆内还是元空间内

类的基本信息instanceKlass

描述 Java 类在虚拟机内部表现形式的核心类,它承载了类的元数据信息,如类名、继承关系、字段、方法等。在类加载过程中,HotSpot 虚拟机将字节码文件中的类信息解析并存储到instanceKlass实例里。在 Java 代码中,Object类、自定义类等,在虚拟机内部都由instanceKlass来表示

初始化源码(jdk 1.8,下同):

InstanceKlass* InstanceKlass::allocate_instance_klass(ClassLoaderData* loader_data,int vtable_len,int itable_len,int static_field_size,int nonstatic_oop_map_size,ReferenceType rt,AccessFlags access_flags,Symbol* name,Klass* super_klass,bool is_anonymous,TRAPS) {int size = InstanceKlass::size(vtable_len, itable_len, nonstatic_oop_map_size,access_flags.is_interface(), is_anonymous);// AllocationInstanceKlass* ik;if (rt == REF_NONE) {if (name == vmSymbols::java_lang_Class()) {// 省略其它代码} else {// normal classik = new (loader_data, size, THREAD) InstanceKlass(vtable_len, itable_len, static_field_size, nonstatic_oop_map_size, rt,access_flags, is_anonymous);}} else {// 省略其它代码}// 省略其它代码// Add all classes to our internal class loader list here,// including classes in the bootstrap (NULL) class loader.loader_data->add_class(ik);Atomic::inc(&_total_instanceKlass_count);return ik;
}

类对象instanceMirrorKlass

instanceKlass的一个特殊子类,专门用于表示 Java 中的java.lang.Class类的实例。每一个 Java 类在虚拟机中都有一个对应的java.lang.Class实例,这个实例由instanceMirrorKlass来描述。instanceMirrorKlass为 Java 代码提供了反射机制的基础支持,通过它可以获取类的各种信息、调用类的方法等

instanceOop InstanceMirrorKlass::allocate_instance(KlassHandle k, TRAPS) {// Query before forming handle.int size = instance_size(k);KlassHandle h_k(THREAD, this);instanceOop i = (instanceOop)CollectedHeap::obj_allocate(h_k, size, CHECK_NULL);// Since mirrors can be variable sized because of the static fields, store// the size in the mirror itself.java_lang_Class::set_oop_size(i, size);return i;
}

 总结:类对象保存在堆内

三、Java8为什么用元空间替换永久代

通用问题的解答步骤:

  1. 原方案的缺点;

  2. 新方案如何解决;

  3. 新方案还有什么缺陷,如何优化(进阶)

永久代的缺点

永久代位于堆内存中

它和 Java 堆中的新生代(Young Generation)、老年代(Old Generation)共同构成了 Java 堆的整体内存布局。

类卸载的条件非常苛刻

类的卸载条件:

  1. 该类的所有实例都已经被垃圾回收。

  2. 加载该类的类加载器已经被垃圾回收

  3. 该类对应的 java.lang.Class 对象没有在任何地方被引用

影响堆的垃圾回收性能

永久代的垃圾回收相对复杂,因为它不仅要管理类的元数据,还要处理常量池等信息。而且永久代的垃圾回收与堆的垃圾回收是相互关联的,可能会影响堆的垃圾回收性能。例如,在进行 Full GC 时,需要同时回收永久代和堆中的对象,增加了垃圾回收的停顿时间。

动态字节码技术的发展

动态字节码控制不当容易造成永久代内存溢出

元空间的优点

元空间使用本地内存来存储

理论上空间无限

提高gc效率

元空间 GC 独立触发,减少堆内存 STW 时间

可以参(chao)考(xi)其它jvm的实现

这是我猜的,不一定对😁

元空间的缺点

碎片化

四、什么是碎片化

一句话:切割分配,归还无序

  1. 假设初始内存为10单元,第一次分配1个单元,此时内存被分为0-1、1-10
  2. 第二次分配2个单元,此时内存为0-1、1-3、3-10
  3. 第三次分配3个单元,此时内存为0-1、1-3、3-6、6-10
  4. 后面即使内存使用完毕释放,内存也被切割为4份不连续的地址

🤔疑问:如何解决碎片化问题?内存合并算法

五、long、double类型是否有线程不安全

long/double占用的空间:8B

Java 内存模型将 64 位的数据的读和写操作分拆为两个 32 位的操作来进行(为什么?)

总结:有,通过volatile 指定原子操作

六、数组的最大长度是多少

具体位置与指针压缩有关:

理论长度是2的32次方 - 1,但数组长度的类型是int,int 类型是 32 位有符号整数,由于数组长度不能为负数,所以理论上 Java 数组的最大长度是 2147483647(即 Integer.MAX_VALUE)

总结:理论长度为2147483647

七、指针压缩

条件

  • 64位系统

  • 8字节对齐

  • 最大堆内存在32G以下

原理

逻辑地址:

test1:0 - 10000

test2:10000 - 110000

test3:110000 - 1100000

为何是32G以下

压缩后的 32 位指针最多可以表示 2的32次方 个不同的值,乘以 8 字节的对齐单位后,最大可以表示的内存空间为 32GB。

🤔疑问:如何突破32G,zgc是否兼容指针压缩?

八、java9 String底层数组为什么从char类型改为byte,改了会带来什么问题,如何解决

节省空间

char:2B

byte:1B

如何确定字符编码

九、ygc如何判断老年代是否引用了新生代的对象

卡表

十、gc延时计算

[Times: user=0.00 sys=0.00, real=0.00 secs]

user:用户态时间

sys:内核态时间

real:实际花费的时间

real = gc发生的时长(所有阶段加起来) + 所有线程到达安全点的时间

public class TestBlockingThread {static Thread t1 = new Thread(() -> {while (true) {long start = System.currentTimeMillis();try {Thread.sleep(1000L);} catch (InterruptedException e) {e.printStackTrace();}long cost = System.currentTimeMillis() - start;//按照正常情况,t1线程,大致上应是每隔1000毫秒左右,会输出一句话 我们使用 cost 来记录实际等待的时间//如果实际时间cost大于1010毫秒 我们就使用System.err输出,也就是红色字样的输出,否则则是正常输出(cost > 1010L ? System.err : System.out).printf("thread: %s, costs %d ms\n", Thread.currentThread().getName(), cost);}});static Thread t2 = new Thread(() -> {while (true) {//下面是一个counted loop,单次循环末尾不会被加入安全点,整个for循环期执行结束之前,都不会进入安全点//存在这样一种情况, 如果某次for循环才刚刚开始没多久, 因为内存过多而需要进行垃圾收集//而我们知道,垃圾收集刚开始的时候需要先获取所有根节点,而根节点的获取依赖所有线程抵达安全点//线程t1很简单,只需要隔1s就会进入安全点,之后,线程t1需要等到其他线程(t2)也进入到安全点//而t2此时才刚刚是for循环的刚开始,所以需要消耗大量时间走完剩下的循环次数,这也就是为什么有时候t1实际cost时间多达5s的原因//也就是gc发生时,要获取所有根节点,而想要获取根节点,就要所有线程抵达安全点,已经抵达的线程(t1)需要等待未抵达的线程(t2)到达安全点 然后才会继续垃圾收集的剩下内容for (int i = 1; i <= 1000000000; i++) {boolean b = 1.0 / i == 0;}try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}}});private static final int _50KB = 3000 * 1024;//下面的代码在创建大量的对象, 一定会导致隔一段时间会出现垃圾收集static Thread t3 = new Thread(() -> {while (true) {try {Thread.sleep(5);} catch (InterruptedException e) {e.printStackTrace();}byte[] bytes = new byte[_50KB];}});public static void main(String[] args) throws InterruptedException {t1.start();Thread.sleep(1500L);t2.start();t3.start();}
}

运行结果:

http://www.lryc.cn/news/603959.html

相关文章:

  • Arduino声控RGB矩阵音乐节奏灯DIY全攻略
  • Nuxt3 全栈作品【通用信息管理系统】菜单管理
  • 比肩 7B 表现!Ovis-U1-3B 集多模态理解、图像生成与编辑于一体
  • 《嵌入式C语言笔记(十五):字符串操作与多维指针深度解析》
  • Go进阶:流程控制(if/for/switch)与数组切片
  • ORACLE的用户维护与权限操作
  • 火山方舟使用豆包基模 —— 基础流程
  • 什么是ios企业签名?
  • ROUGE-WE:词向量化革新的文本生成评估框架
  • H.264视频的RTP有效载荷格式(翻译自:RFC6184 第5节 RTP有效载荷格式)
  • 自然语言处理NLP(3)
  • 烟草复杂包装识别准确率↑31%!陌讯多模态SKU检测算法在零售终端的实战解析
  • CMake 完全实战指南:从入门到精通
  • MySQL的JDBC编程
  • Seq2Seq学习笔记
  • 【绘制图像轮廓】——图像预处理(OpenCV)
  • idea运行tomcat日志乱码问题
  • CentOS安装ffmpeg并转码视频为mp4
  • 编程算法在金融、医疗、教育、制造业等领域的落地案例
  • 单片机(STM32-WIFI模块)
  • windows电脑如何截屏 windows电脑截屏教程汇总
  • 【机器学习深度学习】DeepSpeed框架:高效分布式训练的开源利器
  • Python Flask: Windows 2022 server SMB账户(共享盘账户)密码修改
  • 影刀RPA_初级课程_玩转影刀自动化_EXCEL操作自动化
  • 数据结构(5)单链表算法题(中)
  • 第二十二天(数据结构,无头节点的单项链表)
  • 期刊基础学习
  • 抵御酒店管理系统收银终端篡改攻击 API 加密的好处及实现——仙盟创梦IDE
  • 携全双工语音通话大模型亮相WAIC,Soul重塑人机互动新范式
  • BitMart 启动中文品牌“币市”:引领加密资产本地化发展新篇章