来源:segmentfault.com/u/jack3021
JVM篇
1.JVM垃圾回收的时候如何确定垃圾?是否知道什么是GC Roots?
GC roots就是一组必须活跃的引用
java使用了可达性分析的方法来判断对象是否是垃圾。
基本思路就是从GC Roots对象作为起点向下搜索,如果一个对象到GC Roots没有任何的引用链时候,则说明对象不可用。
2. 可以作为GC Roots的对象有哪些?
虚拟机栈。方法区中的类静态属性的对象。方法区中常量引用的对象。本地方法栈中native方法引用的对象。
3.如何盘点查看JVM系统默认值
- Boolean类型 公式:-XX:+或者-某个属性值 +代表开启 -代表关闭 例子:jinfo -flag PrintGCDetails 端口 -XX:-PrintGCDetails
- KV设值类型 公式:-XX:属性key=属性值value 例子:-XX:MetaspaceSize=128m
java -XX:+PrintFlagsInitial 查看初始值 java -XX:+PrintFlagsFinal 查看最终值
4.如何在运行java命令时候,同时打印参数
java -XX:+PrintFlagsFinal -XX:MetaspaceSize=512m 运行类名
5.新生代转移到老年代的触发条件
- 长期存活的对象。
- 大对象直接进入老年代。
- 在年轻代GC后仍然放不下的对象。
- 动态年龄判断 ,大于等于某个年龄的对象超过了survivor空间一半。
6.什么时候发生YoungGC和FulGC
- Eden 空间占满了, 会触发 Young GC
- Minor(Scavenge) GC 后仍然存活的对象会被复制到 S0 (survivor 0)中去。这样 Eden 就被清空可以分配给新的对象。又触发了一次 Minor GC,
S0 和 Eden 中存活的对象被复制到 S1 中, 并且 S0和 Eden 被清空。在同一时刻, 只有 Eden 和一个 Survivor Space 同时被操作。当每次对象从 Eden 复制到 Survivor Space 或者从 Survivor Space 中的一个复制到另外一个, 有一个计数器会自动增加值。默认情况下如果复制发生超过 15.次, JVM 会停止复制并把他们移到老年代中去. 同样的如果一个对象不能在 Eden 中被创建, 它会直接被创建在老年代中。 - FulGC :
- a) 老年代(Tenured)被写满;
- b) 元空间满;
- System.gc()被显示调用;
- 上一次GC之后Heap的各域分配策略动态变化。
7.类加载过程是什么?
类的加载分为 加载、链接 (包括验证、准备、解析三步)、初始化。
- 加载是文件到内存的过程。通过类的完全限定名查找此类字节码文件,并利用字节码文件创建一个Class对象。
- 验证是否符合虚拟机要求,保证不会危害虚拟机自身安全,包含四种:文件格式验证,元数据验证,字节码验证,符号引用验证。
准备阶段是进行内存分配。为类变量也就是类中由static修饰的变量分配内存,并且设置初始值,这里要注意,初始值是0或者null,而不是代码中设置的具体值,代码中设置的值是在初始化阶段完成的。另外这里也不包含用final修饰的静态变量,因为final在编译的时候就会分配了。
解析主要是解析字段、接口、方法。主要是将常量池中的符号引用替换为直接引用的过程。直接引用就是直接指向目标的指针、相对偏移量等。 - 初始化就是完成对静态块和静态变量赋值。
8.对象的加载过程?
①类加载检查: 虚拟机遇到一条 new 指令时,首先将去检查这个指令的参数是否能在常量池中定位到这个类的符号引用,并且检查这个符号引用代表的类是否已被加载过、解析和初始化过。如果没有,那必须先执行相应的类加载过程。
举个例子,我们的string就算是对象创建,既是 String str= new String("abc"); 首先也会在常量池中定位是否存在值。若不存在,则会在常量池也建立一个abc.
②分配内存: 在类加载检查通过后,接下来虚拟机将为新生对象分配内存。对象所需的内存大小在类加载完成后便可确定,为对象分配空间的任务等同于把一块确定大小的内存从 Java 堆中划分出来。分配方式有 “指针碰撞” 和 “空闲列表” 两种,选择那种分配方式由 Java 堆是否规整决定,而Java堆是否规整又由所采用的垃圾收集器是否带有压缩整理功能决定。
③初始化零值: 内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值(不包括对象头),这一步操作保证了对象的实例字段在 Java 代码中可以不赋初始值就直接使用,程序能访问到这些字段的数据类型所对应的零值。
④设置对象头: 初始化零值完成之后,虚拟机要对对象进行必要的设置,例如这个对象是那个类的实例、如何才能找到类的元数据信息、对象的哈希吗、对象的 GC 分代年龄等信息。 这些信息存放在对象头中。 另外,根据虚拟机当前运行状态的不同,如是否启用偏向锁等,对象头会有不同的设置方式。
⑤执行 init 方法: 在上面工作都完成之后,从虚拟机的视角来看,一个新的对象已经产生了,但从 Java 程序的视角来看,对象创建才刚开始, 方法还没有执行,所有的字段都还为零。所以一般来说,执行 new 指令之后会接着执行 方法,把对象按照程序员的意愿进行初始化,这样一个真正可用的对象才算完全产生出来。
9.GC垃圾回收四大算法?
- 引用计数法
- 复制算法
年轻代中Minor GC使用的就是复制算法。 - 标记清除(Mark-Sweep)
老年代 一般是由标记清除,或者是标记清除和标记整理实现。
先扫描需要被回收的对象,然后标记起来,进行整体清除。
优点:不需要额外的空间。
缺点:会造成内存碎片。 - 标记压缩(Mark-Compact)
老年代 一般是由标记清除,或者是标记清除和标记整理实现。
serial 串行垃圾回收器 :它为单线程环境设置且只使用一个线程进行垃圾回收,会暂停所有用户线程。不适于用服务器。
Parallel 并行垃圾回收器:多个垃圾收集线程并行工作,此时用户线程是暂停的,适用于弱交互场景。
CMS 并发标记清除 :用户线程和垃圾收集线程同时执行,不一定是并行也可能是交替执行。适用于对响应时间有要求的场景。
10.怎么查看默认的垃圾收集器是哪个?
java -XX:+PrintCommandLineFlags -version
11. 串行垃圾回收器:Serial
只使用一个线程进行垃圾回收,会暂停所有用户线程。是java虚拟机运行在32位client模式下的默认新生代垃圾收集器。
优点:简单高效。
缺点:需要暂停其他线程。
对应的JVM参数是-XX:+UseSerialGC
开启后就会在Young区和Old区使用。
新生代使用的是复制算法,老年代使用的是标记整理(压缩)算法。
12. ParNew(并行)收集器
使用多线程进行垃圾回收,在垃圾收集时,会Stop-The-World暂停其他所有工作线程直到他收集结束。
ParNew收集器其实就是Serial新生代
的多线程版本老年代还是单线程。
对应的JVM参数是-XX:+UseParNewGC
开启后只影响新生代的收集,不影响老年代。
新生代使用的是复制算法,老年代使用的是标记整理(压缩)算法。
13. Parallel Scavenge 并行收集器
是一个新生代的垃圾收集器,使用的是复制算法。
在新生代和老年代都是并行化。
JDK8使用的就是 Parallel + Parallel Old 收集器
UseParallelGC 即 Parallel Scavenge 和 Parallel Old。
对应的JVM参数是-XX:+UseParallelGC 或者 -XX:+UseParallelOldGC
开启该参数后:新生代使用的是复制算法,老年代使用的是标记整理(压缩)算法。
14. CMS 收集器(Concurrent Mark Sweep:并发标记清除)
是一种以获取最短回收停顿时间为目标的收集器。
并发收集低停顿,可以和用户线程一起执行。
对应的JVM参数是-XX:+UseConcMarkSweepGC 开启后自动将-XX:+UseParNewGC打开。
使用ParNew(Young区)+CMS(Old区)+ Serial Old收集器组合,Serial Old 将作为CMS出错的后备收集器。
CMS的四步过程:
- 初始标记 :只是标记一下GC Roots能关联的对象,速度很快,仍然需要暂停其他线程。
- 并发标记:进行GC Roots跟踪的过程,和用户线程一起工作,不需要暂停其他线程。主要标记全部对象。
- 重新标记:为了修改在并发标记期间,发生变动的对象,进行重新标记,需要暂停所有线程。
- 并发清除:清除GC Roots中不可达的对象,不需要暂停其他线程。
优点:并发收集低停顿
缺点:并发执行,对CPU资源压力大。采用的是标记清除算法,会产生内存碎片。
## 15. G1 收集器
是一个可以在年轻代和老年代一起使用的垃圾收集器,可以并发执行,设计的目的是为了代替CMS收集器
G1采用的是标记整理(压缩)算法,局部是通过复制算法,不会产生内存碎片。
优点:
- G1是一个有整理内存过程的垃圾收集器,不会产生很多内存碎片。
- G1的STW更可控,在停顿时间上增加了预测机制,用户可以指定期望停顿的时间。
- 将内存划分为多个独立的子区域,其内部在小范围内还是要进行年轻代和老年代的区分。
底层原理:
- 每个块的大小为1MB-32MB,最多有2048个区域。
- 如果有大对象,默认直接分配到老年代,G1划分了一个Humongous区来存放,如果一个H区不够的话,会找连续的H区来存放。为了能找到连续的H区,有时候不得不Full GC。
## 16. 生产环境如果CPU过高,怎么定位?
先用top命令查看cpu占比最高的,然后用jps来查看是那一个后台进程。
jps -l
然后使用
ps - mp 进程ID -o THREAD,tid,time
查看是具体哪个线程占比最高。
然后使用
printf "%x\n" 线程ID
得到转换后的16进制格式 小写。
然后使用
jstack 进程ID | grep 线程id -A60
## 17.什么是双亲委派机制?
java虚拟机对class文件采用的是按需加载,只有当需要用到的时候才会加载到内存生成class对象,采用的是双亲委派机制,把请求交给父类处理。是一种任务委派模式。
优点:
- 避免类的重复加载。
- 避免核心API被篡改。