当前位置: 首页 > 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.xdnf.cn/news/1207207.html

相关文章:

  • 【lucene】currentFrame与staticFrame
  • 落霞归雁思维框架应用(十) ——在职考研 199 管综 + 英语二 30 周「顺水行舟」上岸指南
  • 26考研11408数据结构
  • 电脑没有声音了怎么恢复 快速解决音频故障
  • 艾利特机器人:光伏机器人如何重塑清洁能源制造新格局
  • HarmonyOS-ArkUI Web控件基础铺垫6--TCP协议- 流量控制算法与拥塞控制算法
  • 道路坑洞检测数据集介绍8300张图片-智能道路巡检系统 车载安全监测设备 城市基础设施管理
  • QFutureWatcher 收不到 finished 信号-QFutureWatcher 与对象生命周期
  • Mac m系列芯片安装node14版本使用nvm + Rosetta 2
  • 【Rust并发集合】如何在多线程中并发安全地使用集合
  • vue3插槽详解
  • Deep Research(信息检索增强)认识和项目实战
  • 设计模式---单例
  • 博物馆 VR 导览:图形渲染算法+智能讲解技术算法实现及优化
  • 【MySQL】从连接数据库开始:JDBC 编程入门指南
  • 如何从 Web2 转型到 Web3
  • 01 基于sklearn的机械学习-机械学习的分类、sklearn的安装、sklearn数据集、数据集的划分、特征工程中特征提取与无量纲化
  • 使用JavaScript实现轮播图的任意页码切换和丝滑衔接切换效果
  • Linux之网络部分-应用层协议 HTTP
  • GaussDB 数据库架构师修炼(十) 性能诊断常用视图
  • BGP高级特性之ORF
  • 【C++】第十九节—一文万字详解 | AVL树实现
  • C51 中断
  • mangoDB面试题及详细答案 117道(026-050)
  • 电商修图,尺寸修改适配各大电商平台
  • 第17章——多元函数积分学的预备知识
  • VMware Workstation Pro 详细安装步骤
  • Linux->模拟实现 fopen/fwrite/fclose
  • jQuery 最新语法大全详解(2025版)
  • Uniswap V2 成功上线 PolkaVM:Polkadot Hub 的里程碑时刻