目录
[1简述一下 JVM 的内存模型?](#1简述一下 JVM 的内存模型?)
[3简述 Java 的对象结构?](#3简述 Java 的对象结构?)
[7、 JVM 中的即时编译器(J IT)如何工作?](#7、 JVM 中的即时编译器(J IT)如何工作?)
[8、什么是内存屏障?JVM 如何使用内存屏障保证指令执行序?](#8、什么是内存屏障?JVM 如何使用内存屏障保证指令执行序?)
[9、如何排查和解决 JVM 内存泄漏问 题?有哪些常用的工具和方法?](#9、如何排查和解决 JVM 内存泄漏问 题?有哪些常用的工具和方法?)
1简述一下 JVM 的内存模型?
JVM的内存模型分为线程共享区域和线程私有区域
其中共享区域有堆和方法区
私有区域有程序计数器,虚拟机栈,本地方法栈
堆主要用于存储对象的实例和数组
其中内存主要分为新生代和老年代,其中新生代又分为伊甸园区和两个幸存区
方法区用于存储类信息,常量,静态变量以及JIT编译后的代码
在JDK1.7时,使用的是永久代
在之后,使用的是元空间,直接使用本地内存,不再受堆内存的影响
包含子区域 :
运行时常量池,存放类,方法,字段的符号引用和字面量
程序计数器:存储下一条待执行指令的地址
虚拟机栈存储方法调用的栈帧,
栈帧又分为:局部变量表,操作数栈,动态链接,返回地址
局部变量表存储方法的参数和局部变量
操作数栈是执行指令的工作区
动态链接指向方法区该方法的符号引用
返回地址:方法退出后回到的指令地址
本地方法栈:
为JVM调用Native方法服务
2说说堆和栈的区别?
1根本属性上
栈是线程私有的内存区域,每次创建进程,都会创建一个栈,存储的是栈帧
堆事线程共享的内存区域,JVM启动,一个JVM进程只能有一个堆,存储的是实例的对象和数组
2存储内容上:
栈存储的是栈帧
堆存储的是实例的对象和数组
3生命周期上:
栈的生命周期自动管理,与作用域绑定:
堆的生命周期与垃圾自动回收机制有关
4线程安全性上面:
栈天然线程安全,因为每个线程都私有自己的栈
堆线程不安全,因为它被同一个进程的所有线程共享
5碎片化上:
栈没有内存碎片,因为它的先进后出属性,保证了分配和释放的顺序性
堆有内存碎片,频繁创建和销毁不同大小的对象会导致内存中出现许多不连续的小空闲区域。
3简述 Java 的对象结构?
Java对象在内存中的结构主要由三部分组成:对象头,实例数据和对齐填充:
对象头包含两类关键信息:Mark word和类型指针;Mark word存储对象运行时的元数据.类型指针指向方法区中对象的类元数据,JVM可以通过它确定对象属于哪个类
实例数据:存储对象的所有字段信息
对齐填充:JVM要求对象的大小必须是8字节的整数倍,这样可以提高内存的访问效率,因为CPU是分块读取的
4、如何判断对象可以被回收?
1从可达性进行分析:从GC roots对象出发,遍历引用链,若对象与GC root之间无路径,则可回收
2四种引用决定回收策略:
强引用:永不回收
软引用:内存不足时回收
弱引用:触发GC回收时回收
虚引用:任何时候可回收
3JVM堆内存分为不同的区域,对应不同的回收策略
新生代
新对象创建在伊甸园区,进过 年轻代回收之后进入幸存区,经过15次年轻代回收之后进入老年代
老年代
存放长期存活对象
触发全堆回收时回收
元空间
存放类元数据
类卸载时回收相关对象
5、你知道哪些垃圾收集算法?
一、基础垃圾收集算法
Java 虚拟机主要采用以下几种基础垃圾收集算法:
1、标记-清除算法(Mark-Sweep):
分为标记和清除两个阶段
标记阶段:识别所有需要回收的对象
清除阶段 :回收被标记对象占用的空间
缺点:产生内存碎片 ,执行效率随对象数量增加而降低
2、标记-复制算法(Copying):
将内存分为大小相等的两块 ,每次只使用一块 垃圾回收时将存活对象复制到另一块内存
优点:无内存碎片 ,适合对象存活率低的区域 缺点: 内存利用率只有 50%
3、标记-整理算法(Mark-Compact):
标记阶段与标记-清除相同
整理阶段:将所有存活对象向一端移动 优点:避免内存碎片问题
缺点:整理过程耗时较长
二、现代垃圾收集器的算法组合现代 JVM 垃圾收集器通常混合使用多种算法以适应不同场景:
1、 Parallel Scavenge:
新生代:复制算法
老年代:标记-整理
2、CMS(Concurrent Mark-Sweep):
新生代:复制算法
老年代:并发标记-清除
3、G1 垃圾回收器:
将堆划分为多个 Region Young GC 使用复制算法
Mixed GC 使用标记-整理和复制算法混合
6虚拟机为什么使用元空间替换了永********久代?
JVM从jdk8开始使用元空间替换永久代,永久代是JVM存储类元数据的内存空间,有以下几个问题:
1内存溢出问题:永久代大小固定,当加载大量数据时候容易内存溢出
2垃圾回收效率低:永久代的回收与老年代绑定,且元数据回收逻辑复杂,容易触发全堆垃圾回收;
3与JVM过度耦合,永久代是HotSpot虚拟机特有的,其他JVM并没有,不利于Java跨虚拟机同意标准
使用元空间的优势:因为元空间本质是使用本地内存,所以
1内存动态扩展,因为元空间使用的是本地内存,没有空间上限,所以不会内存溢出
2垃圾回收效率高,因为元空间的回收与老年代是独立的,仅针对无用类进行回收,减少了全堆垃圾回收的效率;
3与内存本地整合:因为元空间使用了操作系统的本地内存,这样不会使JVM的内存与本地内存进行隔离;
4简化了JVM的配置
7、 JVM 中的即时编译器( J IT )如何********工作?
JVM中的即时编译器是Java实现高性能的关键组件之一,通过一下几个环节进行工作:
1解释执行与编译执行相结合:Java通过解释器逐行解释字节码执行,这样启动快但执行效率低,这时JIT会将频繁执行的热点代码直接编译为本地机器码,后续直接执行本地机器码;
2热点代码的识别:
JVM通过热点代码探测器识别热点代码:根据方法的调用次数和循环次数进行判断
3分层编译策略:常采用双层编译器的策略,代码先被c1编译器快速编译,然后执行过程中频繁使用的热点代码再被C2编译器编译成本地机器码
4编译优化技术:即时编译优化,循环优化和冗余消除
5将编译后机器码进行缓存,下次使用直接调用即可
8、什么是内存屏障?JVM 如何使用内 存屏障保证指令执行序?
内存屏障是一种CPU指令或JVM层面的同步机制,保证特定指令的顺序执行以及内存操作的可见性,防止编译器,cpu指令重排优化对多线程下的程序的正确性造成影响;
JVM主要在实现volatile,锁,final时插入内存屏障
写写屏障:保证前面的写操作先执行,再执行后面的写操作
读写屏障:保证写对其他线程可见
读读屏障:保证 读操作不被重排
在 JVM 里:
对volatile变量的写操作之前加释放屏障 ,写完之后加刷新屏障,使结果立即 同步到主内存
读之前加加载屏障,强制读取主内存的最新数据
最终到达:禁止指令重排和内存操作可见性
9、如何排查和解决 JVM 内存泄漏问****题?有哪些常用的工具和方法?
排查JVM内存泄漏的核心步骤:定位是否内存泄漏 ->定位泄漏对象->定位引用链->问题修复
1先确认现象:频繁full gc,老年代越来越大,gc时间越来越长,最终OOM,基本就可以确认是内存泄漏
2导出堆dump文件:可以手动导出,也可以使用JVM自带的工具导出
3分析堆快照:看看哪个对象异常多,占用内存大,存活时间长不能被回收
4查看GC Roots引用链:找到是谁一直在引用这些异常对象,导致无法GC
5定位代码问题并修复:
常见的问题:静态集合缓存未清理,threadLocal未remove,资源未关闭和监听器未移除
常用工具:
jps:查看Java进程的ID
jstat:查看GC情况,判断内存是否泄漏
jmap:导出堆dump文件
jhat:简单分析堆
MAT:最常用,专业分析泄漏,找引用链
Arthas:快速线上问题排查
末尾页
JVM内存模型分为线程共享区域(堆、方法区)和私有区域(程序计数器、虚拟机栈、本地方法栈)。堆存储对象实例,分为新生代(伊甸园、幸存区)和老年代;方法区存储类信息等,JDK8后改为元空间。对象结构包括对象头(Markword和类型指针)、实例数据和对齐填充。垃圾回收通过可达性分析判断对象可回收性,采用标记-清除、标记-复制等算法。JVM使用元空间替代永久代解决内存溢出问题,通过JIT编译器将热点代码编译为机器码提升性能。内存屏障用于保证多线程下的内存可见性和指令顺序。排查内存泄漏可通过分析堆dump和GC日
