ReZero's Utopia.

JVM notes

Word count: 1.4kReading time: 7 min
2020/09/18 Share

How java code run?

ab5c3523af08e0bf2f689c1d6033ef77

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ echo '
public class Foo {
public static void main(String[] args) {
boolean flag = true;
if (flag) System.out.println("Hello, Java!");
if (flag == true) System.out.println("Hello, JVM!");
}
}' > Foo.java
$ javac Foo.java
$ java Foo
$ java -cp ./asmtools-6.0/lib/asmtools.jar org.openjdk.asmtools.jdis.Main Foo.class > Foo.jasm.1
$ awk 'NR==1,/iconst_1/{sub(/iconst_1/, "iconst_2")} 1' Foo.jasm.1 > Foo.jasm
$ java -cp ./asmtools-6.0/lib/asmtools.jar org.openjdk.asmtools.jasm.Main Foo.jasm # recompile
$ java Foo

Check the change:

1
2
3
4
5
$ diff Foo.jasm Foo.jasm.1 
18c18
< iconst_2;
---
> iconst_1;

Java basic types

basic types

Class Loader

java_classloader_hierarchy

load

tips: In this time,JVM has generated the class structure and write in the method area

  1. Every class loader inherit java.lang.ClassLoader except boot class loader.

    • Boot: jre/lib, Xbootclasspath
    • Extension: ext, dirs
    • Application:
  2. JAVA9: change extension to platform which would load almost all modules except important moudles such as java.base.

  3. Class unique ID: result of class loader object and class full path name.

tips: get the address of method instead of class.

  1. Valid byte for jvm criterion.

  2. Prepare

    • Allocate memory
    • Construct layer about class such as method table which realize dynamic binding of virtual method.
  3. analyze

    • resolve these symbols to actual refference

Besides: Java machine specification not require finish the analyze in the link process, it just has provisions that the necessity of resolving symbol refference before execute the corresponding byte code.

init process

  1. final static [basic type or String] will be signed ConstantValue and init directly by jvm. Besides, all of directly assignment and static block code will be put in the same method and rename as <clinit>.

  2. JVM will use block method to ensure teh clinit method only be executed once.(And the feature always be used to realized singleton lazy init process)

  3. When will the init process be triggered:

    • JVM start, init user settle Main class
    • new the aim class
    • instructions: call static method, init over class
    • instructions: visit static field init over class
    • child class init trigger parent class init
    • default method defined in interface will result in interface init process.
    • reflection init class
    • the first call MethodHandle object, init the class where methodHandle locate.
1
2
3
4
5
6
7
8
9
public class Singleton {
private Singleton() {}
private static class LazyHolder {
static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return LazyHolder.INSTANCE;
}
}

The above code:
Only class Singleton.getInstance, program will visit LazyHolder.INSTANCE, then trigger the process of LazyHolder init and create a Singleton object.

Practice:

JVM: -verbose:class will print the order of class loading and special symbol before LazyHolder method.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

$ echo '
public class Singleton {
private Singleton() {}
private static class LazyHolder {
static final Singleton INSTANCE = new Singleton();
static {
System.out.println("LazyHolder.<clinit>");
}
}
public static Object getInstance(boolean flag) {
if (flag) return new LazyHolder[2];
return LazyHolder.INSTANCE;
}
public static void main(String[] args) {
getInstance(true);
System.out.println("----");
getInstance(false);
}
}' > Singleton.java
$ javac Singleton.java
$ java -verbose:class Singleton

Problem:
new array whether result in LazyHolder load or init?

A: load not init.

1
2
3
4
$ java -cp /path/to/asmtools.jar org.openjdk.asmtools.jdis.Main Singleton\$LazyHolder.class > Singleton\$LazyHolder.jasm.1
$ awk 'NR==1,/stack 1/{sub(/stack 1/, "stack 0")} 1' Singleton\$LazyHolder.jasm.1 > Singleton\$LazyHolder.jasm
$ java -cp /path/to/asmtools.jar org.openjdk.asmtools.jasm.Main Singleton\$LazyHolder.jasm
$ java -verbose:class Singleton

Problem2:
new array will result in link of LazyHolder?

A: No.(while getInstance(false) will link and init)

How do JVM call method?

1
2
3
4
5
6
7
void invoke(Object obj, Object... args) { ... }
void invoke(String s, Object obj, Object... args) { ... }

invoke(null, 1); // call second
invoke(null, 1, 2); // call second
invoke(null, new Object[]{1}); // call first invoke

jvm 实战

本文内容笔记主要来自

  1. mashibing: http://www.mashibing.com/
  2. 以及深入理解JVM虚拟机
  3. 以及深入拆解Java虚拟机(郑雨迪)极客时间 https://time.geekbang.org/column/article/41245

编译

在 HotSpot 里面,上述翻译过程有两种形式:

第一种是解释执行,即逐条将字节码翻译成机器码并执行;

第二种是即时编译(Just-In-Time compilation,JIT),即将一个方法中包含的所有字节码编译成机器码后再执行。

Java 字节码

基本类型

在 Java 中,正无穷和负无穷是有确切的值,在内存中分别等同于十六进制整数 0x7F800000 和 0xFF800000。
[0x7F800001, 0x7FFFFFFF]和[0xFF800001, 0xFFFFFFFF] 对应的浮点数是 NaN(Not-a-Number)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

public class Foo {
static boolean boolValue;
public static void main(String[] args) {
boolValue = true; // 将这个true替换为2或者3,再看看打印结果
if (boolValue) System.out.println("Hello, Java!");
if (boolValue == true) System.out.println("Hello, JVM!");
}
}

//当替换为2的时候无输出
//当替换为3的时候打印HelloJava及HelloJVM
//猜测是因为将boolean 保存在静态域中,指定了其类型为'Z'
//当修改为2时取低位最后一位为0,当修改为3时取低位最后一位为1则说明boolean的掩码处理是取低位的最后一位

类加载 查找字节流的过程

双亲委派模型

三个GC算法

  1. mark sweep: 标记清除,标记后进行清除,所以碎片化

  2. copy: 标记后统一整理放到一侧,然后对另一侧大扫除。由于 Eden 的特点是大量产生,大量销毁,因此很适用copy。使用时只需将Eden 少量存活的copy到 survivor1 中,然后对 Eden
    区进行清理即可。 等到二次回收时会将 eden 区的以及 survivor1 的一起复制到 survivor2 当中去。

  1. mark compact: 一边清一边压缩,时刻保持内存紧致。对于老年代这种对象一般都存活很久的就很适用。

10 个 Garbage collector

  • Epsilon: 跟进追踪,不做任何回收,常用来 debug 或者不需要回收的场景

分代模型

基本命令

  1. top 查内存占用

  1. jps 列出系统中java 的进程

  2. jinfo pid 拿到基本信息

  3. jstack pid | more 罗列出所有线程的调用栈状态

上面仨命令常用来定位 死锁


  1. jstat -gc 不好用,效果不直观

  2. arthas 基本命令:

    • dashboard 类似 top,能给出基本占用信息:如果是JVM没有报OOM便退出了,建议排查堆外内存
    • jvm 类似 jinfo 输出使用 JVM的相关信息比如使用的GC,memory 等
    • thread 类似 jstack,加 -b 参数可用于排查死锁
    • redefine 重定义 class 类似字节码插桩
  3. jmap -histo pid 定位哪些对象在吃内存

    • jmap -dump:format=b.file=xxx.hprof 2366

实际实例 https://mp.weixin.qq.com/s?__biz=Mzg3MTEyMjUyMQ==&mid=2247483765&idx=1&sn=1c20adaae37a9c11bed1c3d2964909d0&chksm=ce821243f9f59b55eec0ebf5beb3d660668151bd46c03ded25c0fd731c3ff188faea3c0b3c53&mpshare=1&scene=23&srcid=0827zNKbMDdpJCoIv9omeEZs&sharer_sharetime=1598580051461&sharer_shareid=162a8816dfe520fecd477f663aaf4f98#rd

OQL 学习

CATALOG
  1. 1. How java code run?
  2. 2. Java basic types
  3. 3. Class Loader
    1. 3.1. load
    2. 3.2. link
    3. 3.3. init process
  4. 4. How do JVM call method?
    1. 4.1. jvm 实战
    2. 4.2. 编译
  5. 5. Java 字节码
  6. 6. 基本类型
  7. 7. 类加载 查找字节流的过程
    1. 7.1. 三个GC算法
    2. 7.2. 10 个 Garbage collector
      1. 7.2.1. 分代模型
      2. 7.2.2. 基本命令
      3. 7.2.3. OQL 学习