什么是字节码?采用字节码的最大好处是什么?什么Java是虚拟机?
java 源代码通过虚拟机编译器编译后产生的文件.class 文件,他不面向任何特定的处理器,直面向虚拟机。- 一次编译到处运行。
Java 虚拟机屏蔽了与具体操作系统平台相关的信息,使得Java 程序只需生成在Java 虚拟机上运行的目标代码字节码,就可以在多种平台上不加修改地运行。
字节码的编译过程
JVM怎么判断对象是可回收对象?有哪些方法。
引用计数法
- 给对象添加一个引用计数器,当有一个地方引用它,计数器值加 1;当引用失效时,计数器值减 1。任何时刻计数器值为 0 表示这个对象可以被回收了。
- 效率高,实现简单,无法解决循环引用。
可达性分析
JVM的内存结构,新生代与老年代的比例,Eden和Survivor比例。
新生代和老年代
图片来自:https://www.cnblogs.com/liuqing576598117/p/10277053.html 
jvm 内存结构

类加载器,可以打破双亲委派么,怎么打破
JVM内存为什么要分成新生代,老年代,持久代
- 对象的存活所时间是不一样的。
- 为了更好的进行垃圾回收。不同的区进行不同的垃圾回收机制,提高效率。
新生代中为什么要分为Eden和Survivor
- 如果没有
Survivor 区,那么Eden 每次满了清理垃圾,存活的对象被迁移到老年区,老年区满了,就会触发Full GC ,Full GC 是非常耗时的。 Jvm 优化主要就是增加Minor Gc 减少FullGc 。
JVM 出现 FullGC 很频繁,怎么去线上排查问题?
原因
- 程序执行了
System.gc() 有可能触发FullGC 。 - 老年代空间不足。
- 方法区空间不足。
解决方法
-XX:HeapDumpBeforeFullGC
-XX:HeapDumpPath=保存dump文件的文件绝对路径
jmap -F -dump:live,file=jmap.hprof [PID]
scp local_file remote_ip:remote_file
JVM中一次完整的GC流程是怎样的,对象如何晋升到老年代
说说你知道的几种主要的JVM参数。
Xms :最小堆内存Xmx :堆的最大内存Xmn :新生代内存
Java对象的创建过程
类初始化顺序
- 父类静态变量、静态初始化块
- 子类静静态变量、静态初始化块
- 父类初始化块、构造方法
- 子类初始化块、构造方法
Jvm参数配置
常用参数
-Xms:初始堆大小(最小堆)。
-Xmx:最大堆大小。
-Xmn:年轻代大小(Sun官方推荐配置为整个堆的3/8)。
-Xss:每个线程的堆大小(在相同物理内存下,减小这个值能生成更多的线程)。
-Xms 和 -Xmx 设置成一致的值可以避免堆自动扩展。
JVM内存大小 = 年轻代大小 + 老年代大小 + 持久代大小(perm)。
G1和cms区别
G1
G1 收集器收集范围是老年代和新生代。不需要结合其他收集器使用。- 可预测垃圾回收停顿时间。
- 使用标记整理算法,降低内存空间碎片。
Cms
CMS 收集器是老年代的收集器,可以配合新生代的Serial和ParNew 收集器一起使用。- 使用标记清除算法,容易产生内存碎片。
打出线程栈信息
- 找出服务进程
id :ps -ef|grep java
sudo -u admin jstack pid > jstack.txt
类加载的执行过程
对象的访问定位有哪两种方式?
User user = new User();
user 表示一个本地引用,存储在栈的本地变量表中,表示一个引用类型的数据。new User() 作为实例对象放在堆中,Java 堆中存放了对象的类型、方法等信息。
使用句柄

直接指针
- 图片来自:https://www.jianshu.com/p/8580ab50e261

jvm 调优的工具
JVM垃圾回收机制,何时触发MinorGC等操作呢?
MinorGC :新生代GC ,对象优先在 eden 创建并区分配内存,当 eden 区内存无法为一个新对象分配内存时,就会触发 MinorGC FullGc :老年代GC ,jvm 调优主要是尽可能增加MinorGC 减少FullGc
对象什么时候会进入老年代?
- 对象年龄达到一定的大小 ,就会离开年轻代, 进入老年代。
- 若对象体积太大, 新生代无法容纳这个对象。
内存泄漏和内存溢出区别?
内存泄漏
- 程序在申请内存后,无法释放已申请的内存空间就造成了内存泄漏,内存泄漏堆积后的后果就是内存溢出。
- 对象被引用,无法回收。例如没有关闭流、集合没有清空
内存溢出
- 指程序申请内存时,没有足够的内存供申请者使用。
- 例如 从数据库中查出大量数据、创建大量的对象没有回收。
什么情况下会发生栈内存溢出。什么时候发生堆溢出?你是怎么排错的?
栈溢出
堆溢出
排错方法
- 生成内存快照文件,分析快照文件。
- 查看程序输出的日志文件。
tomcat类加载机制

JIT
JIT 即时编译:在运行时候将字节码翻译为机器码。C1(Client) :编译速度块,优化方式比较保守。- C2(server):编译速度慢,优化方式比较激进。
C1+C2 (分层编译):在开始阶段采用C1 编译,当代码运行到一定热度之后采用G2 重新编译。
逃逸分析技术
- 在编程语言的编译优化原理中,分析指针动态范围的方法称之为逃逸分析。
- 方法逃逸:对象作为参数进行传递。
- 线程逃逸:类变量、实例变量被不同的线程访问。
调用System.gc()会发生什么?
System.gc() :告诉垃圾收集器打算进行垃圾收集,而垃圾收集器进不进行收集是不确定的。System.runFinalization() : 强制调用已经失去引用的对象的finalize 方法。
MinorGC条件FullGC条件
- 老年代最大的可用连续空间是否大于新生代的所有对象总空间,如果大于则执行
MinorGC - 老年代空间不足触发
FullGC
System.gc()和Runtime.gc()会做什么事情?
public static void gc() {
Runtime.getRuntime().gc();
}
主内存与工作内存
- 主内存是所有的线程所共享的,工作内存是每个线程自己有一个。

内存间交互操作
? Java 内存模型将内存分为工内存和主内存,变量从主内存拷贝到工作内存的过程中,java 内存模型定义了8中操作完成。
- 锁定
lock :锁定主内存中的变量,将变量表示为一个线程独占的状态。 - 解锁
unlock :释放主内存中锁定的变量,释放的变量被其他的线程使用 - 读取
read :将主内存中的变量传输到线程工作内存中。 - 载入
load :将read 的变量放到工作内存的共享变量副本中。 - 使用
use :把工作内存中一个变量的值传递给执行引擎。 - 赋值
assign :把一个从执行引擎接收的值赋给工作内存的变量。 - 存储
store :把工作内存中一个变量的值传送到主内存中,以便随 后的write 操作使用。 - 写入
write :把store 操作从工作内存中得到的变量的值放入主内存的 变量中。
volatile 禁止内存重排序
指令重排序
- 所谓指令重排序,是指计算机在执行程序时,为了提高性能,编译器和处理器常常会对指令进行重排。指令重排必须保证最终执行结果和代码顺序执行结果一致。
- 在单线程中不会有问题,但是在多线程环境下会出问题。
public void test(){
int a = 1;
int b = 2;
int c = a+4;
int d = a*a;
}
volatile 规定禁止指令重排,从而保证数据的一致性。
内存模型三大特性
原子性
- 通过
synchronized 关键字定义同步代码块或者同步方法保障原子性。 - 通过
Lock 保证原子性。 - 通过
Atomic 类型进行原子操作。
可见性
volatile :保证新值能立即同步到主内存,每次使用前立即从主内存中刷新。synchronized :synchronized 关键字在释放锁之前,必须先把此变量同步回主内存中。final :final 修饰的变量,一旦完成初始化,就不能改变。
有序性
volatile 规定禁止指令重排,从而保证数据的一致性。synchronized :某一时刻只有一个线程访问资源。
谈谈先行发生原则
- 先行发生原则是判断数据是否存在竞争、线程是否安全的主要依据。
- 先行发生是
Java 内存,模型中定义的两项操作之间的偏序关系,如果操作A先行发生于操作B,那么操作A产生的影响能够被操作B观察到。
JVM 为什么使用元空间替换了永久代?
-
在Java7及以前的版本,是存在永久代的。在Java7版本时,永久代已经发生了悄悄的变化。等到Java8时,彻底废弃了永久代,由元空间替换。 -
永久代是 HotSpotVM 对方法区的实现,JDK 8 将其移除。 -
类及方法的信息等比较难确定其大小,因此对于永久代的大小指定比较困难,太小容易出现永久代溢出,太大则容易导致老年代溢出。 -
永久代会为 GC 带来不必要的复杂度,并且回收效率偏低。 -
将 HotSpot 与 JRockit 进行整合,JRockit 是没有永久代的。 -
图片来自:https://blog.csdn.net/qq_33591903/article/details/105634782

是堆中的永久代(Perm Gen space)
JVM 内存划分为堆内存和非堆内存,堆内存分为年轻代、老年代,非堆内存就一个永久代。- 堆内存:存放的是对象,垃圾收集器就是收集这些对象,然后根据GC算法回收。
- 非堆内存:永久代,也称为方法区,存储程序运行时长期存活的对象,比如类的元数据、方法、常量、属性等。
- 一般设置为128M就足够。
JVM的永久代中会发生垃圾回收么?
MinorGC 的过程
- 新创建的对象分配到
Eden 区,S0、S1 为空。 - 当
Eden 区满了的时候,minor garbage 被触发。 - 经过扫描标记,存货的对象复制到
S0 不存活的对象被回收。 - 当
Eden 和S0 区空间满了,S0 的所有的数据都被复制到S1 ,对象的年龄+1。 - 经过几次
MinorGC 之后,当存活对象的年龄达到一个阈值之后,就会被从年轻代Promotion 到老年代。 MinorGC 一次又一次的进行,不断会有新的对象被promote 到老年代。
解释 Java 堆空间及 GC
Java 程序在启动的时候,会分配内存,程序中的对象创建的时候,从堆空间中分配内存。Gc 是Jvm 内部的一个进程,回收无效的对象用于将来的分配。
能保证 GC 执行吗?
WeakHashMap 是怎么工作的?
WeakHashMap 使用的是弱引用,因此它的对象可能被随时回收。更直观的说,当使用 WeakHashMap 时,即使没有删除任何元素,它的尺寸、get方法也可能不一样。
@Test
public void testWeakHashMap() {
WeakHashMap<String, String> map = new WeakHashMap<>();
map.put("key_one", "test");
map.put("key_two", "test");
map.put("key_three", "test");
}
垃圾收集器,各自的优缺点
Stop The World 了解过吗?
- 在垃圾回收过程中经常涉及到对对象的挪动(比如对象在
Survivor 0 和Survivor 1 之间的复制),进而导致需要对对象引用进行更新。为了保证引用更新的正确性,Java将暂停所有其他的线程,这种情况被称为Stop-The-World
如何避免OOM
- 使用轻量的数据结构,例如使用
StringBuilder 拼接字符串 - 不用的对象因该立马释放,关闭不用的流
- 使用软引用
JVM调优思路?如何确定它们的大小呢?
调优目标
调优步骤
- 分析GC日志快照文件,判断是否需要优化
- 确定JVM调优参数
- 对比观察调优前后的差异
- 不断的分析和调整,直到找到合适的JVM参数配置
|