On the JVM - memory structure (II) - Virtual Machine Stack | Cool Fan

2.1 Definitions

Java Virtual Machine Stacks(Java Virtual Machine stack)

  • Java Virtual Machine stack Java memory model described methods performed for storing a stack frame is the thread private life cycle with thread starts to produce, thread end and die
  • When a thread is created the virtual machine stack, each method creates a stack frame (Stack Frame) stack in a virtual machine, when executed, for storing local variable table, the operand stack, the dynamic connection, the method returns the address and other information. Each method call to the completion of the execution procedure, it corresponds to the stack (push) a stack frame on the stack of the virtual machine to process the stack (popped) of
  • Each thread can only have one active stack frame corresponds to that method being performed   Discrimination of
  1. Garbage collection is involved stack memory?

    not involving. Stack memory is nothing more than a time of method invocation stack frame memory, there is the stack frame after each method call will be popped off the stack, that is, this memory will be automatically recovered off, so do not need garbage collection to recycling stack RAM.

  2. Stack memory allocation bigger the better it?

    No.

    • Stack memory can be specified by the size parameter in a virtual machine code runs  -Xss size. Not specified, in addition to the windows system, default is 1M, windows system is based on the size of virtual memory allocation.
    • The larger the stack memory allocation, but the method can be performed more times recursive calls, and efficiency will not run faster, but will make fewer number of threads executable (the same total memory, each thread's stack memory becomes large number fewer)
  3. Whether local variables within the method thread safe?

    Whether variables are thread-safe, depending on when this variable is multi-threaded shared the results of each run single-threaded operating results and whether it is the same.

    • If a local variable within the method does not escape the role of access methods, it is thread safe.
    • If the local variable is a reference to an object, and fled the scope of the method, then we need to consider thread safety (basic data types will not have this problem)

    Sample Code 1

    static void m1() { int x=0; for(int i=0;i<500;i++){ x++; } System.out.println(x); } 

    xThe local variables are thread-safe. Each thread corresponds to a stack, then each method call in the thread will have a new stack frame, so the xvariable is in the stack frame stack in different threads, independently of each other, which is thread-safe.

    2 Sample Code

    public static void m1() { StringBuilder sb = new StringBuilder(); sb.append(1); sb.append(2); sb.append(3); System.out.println(sb.toString()); } public static void m2(StringBuilder sb) { sb.append(1); sb.append(2); sb.append(3); System.out.println(sb.toString()); } public static StringBuilder m3() { StringBuilder sb = new StringBuilder(); sb.append(1); sb.append(2); sb.append(3); return sb; } 
    • m1The method sbis thread-safe, because it is a local variable within the thread.
    • m2方法中sb是线程不安全的,因为其是作为方法的参数传递进来,那么就有可能有其他线程能够访问到这个变量,那么这个变量就是多个线程共享的,可能造成值不一致,也就是线程不安全的。
    • m3方法中sb是线程不安全的,虽然其是线程中的局部变量,但是其作为返回值返回了,那么就有可能被其他线程使用,也就是多线程共享,可能造成值不一致,线程不安全。

2.2 栈帧

  • 栈帧存在于 Java 虚拟机栈中,是 Java 虚拟机栈中的单位元素,每个线程中调用同一个方法或者不同的方法,都会创建不同的栈帧(可以简单理解为,一个线程调用一个方法创建一个栈帧),所以,调用的方法链越多,创建的栈帧越多(例如:递归)。每调用一个新的方法,被调用方法对应的栈帧就会被放到栈顶(入栈),也就是成为新的当前栈帧。当一个方法执行完成退出的时候,此方法对应的栈帧也相应销毁(出栈)。 

  • 2.2.1 局部变量表(Local

2.2.1 局部变量表(Local Variable Table)

  • 每个栈帧中都包含一组称为局部变量表的变量列表,用于存放方法参数和方法内部定义的局部变量。在 Java 程序编译成 Class 文件时,在 Class 文件格式属性表中 Code 属性的 max_locals(局部变量表所需的存储空间,单位是 Slot) 数据项中确定了需要分配的局部变量表的最大容量。

  • 局部变量表的容量以变量槽(Variable Slot)为最小单位,不过 Java 虚拟机规范中并没有明确规定每个 Slot 所占据的内存空间大小,只是有导向性地说明每个 Slot 都应该存放的8种类型: byte、short、int、float、char、boolean、reference(对象引用就是存到这个栈帧中的局部变量表里的,这里的引用指的是局部变量的对象引用,而不是成员变量的引用。成员变量的对象引用是存储在 Java 堆(Heap)中)、returnAddress(虚拟机数据类型,returnAddress 类型的值就是指向特定指令内存地址的指针,JVM支持多线程,每个线程有自己的程序计数器(pc register),而 pc 中的值就是当前指令所在的内存地址,即 returnAddress 类型的数据,当线程执行 native 方法时,pc 中的值为 undefined)类型的数据,这8种类型的数据,都可以使用32位或者更小的空间去存储。Java 虚拟机规范允许 Slot 的长度可以随着处理器、操作系统或者虚拟机的不同而发生变化。对于64位的数据类型,虚拟机会以高位在前的方式为其分配两个连续的 Slot 空间。即 long 和 double 两种类型。做法是将 long 和 double 类型速写分割为32位读写的做法。不过由于局部变量表建立在线程的堆栈上,是线程的私有数据,无论读写两个连续的 Slot 是否是原子操作,都不会引起数据安全问题。

  • Java 虚拟机通过索引定位的方式使用局部变量表,索引值的范围是从0开始到局部变量表最大的 Slot 数量。如果是32位数据类型的数据,索引 n 就表示使用第 n 个 Slot,如果是64位数据类型的变量,则说明要使用第 n 和第 n+1 两个 Slot。

  • 在方法执行过程中,Java 虚拟机是使用局部变量表完成参数值到参数变量列表的传递过程。如果是实例方法(非 static方法),那么局部变量表中的第0位索引的 Slot 默认是用来传递方法所属对象实例的引用,在方法中可以通过关键字 this 来访问这个隐含的参数。其余参数按照参数表的顺序来排列,占用从1开始的局部变量 Slot,参数表分配完毕后,再根据方法体内部定义的变量顺序和作用域分配其余的 Slot。

  • 局部变量表中的 Slot 是可重用的,方法体中定义的变量,其作用域并不一定会覆盖整个方法体,如果当前字节码程序计数器的值已经超过了某个变量的作用域,那么这个变量相应的 Slot 就可以交给其他变量去使用,节省栈空间,但也有可能会影响到系统的垃圾收集行为。

  • 局部变量无初始值(实例变量和类变量都会被赋予初始值),类变量有两次赋初始值的过程,一次在准备阶段,赋予系统初始值;另外一次在初始化阶段,赋予开发者定义的值。因此即使在初始化阶段开发者没有为类变量赋值也没有关系,类变量仍然具有一个确定的默认值。但局部变量就不一样了,如果一个局部变量定义了但没有赋初始值是不能使用的。

使用一段代码说明一下局部变量表:

// java 代码
public int test() { int x = 0; int y = 1; return x + y; } // javac 编译后的字节码,使用 javap -v 查看 public int test(); descriptor: ()I flags: ACC_PUBLIC Code: stack=2, locals=3, args_size=1 0: iconst_0 1: istore_1 2: iconst_1 3: istore_2 4: iload_1 5: iload_2 6: iadd 7: ireturn LineNumberTable: line 7: 0 line 8: 2 line 9: 4 LocalVariableTable: Start Length Slot Name Signature 0 8 0 this Lcom/alibaba/uc/TestClass; 2 6 1 x I 4 4 2 y I 

对应上面的解释说明,通过 LocalVariableTable 也可以看出来: Code 属性: stack(int x(1个栈深度)+ int y(1个栈深度))=2, locals(this(1 Slot)+ int x(1 Slot)+ int y(1 Slot))=3, args_size(非 static 方法,this 隐含参数)=1

验证 Slot 复用,运行以下代码时,在 VM 参数中添加 -verbose:gc

public void test() { { byte[] placeholder = new byte[64 * 1024 * 1024]; } int a = 0; // 当这段代码注释掉时,System.gc() 执行后,也并不会回收这64MB内存。当这段代码执行时,内存被回收了 System.gc(); } 

局部变量表中的 Slot 是否还存在关于 placeholder 数组对象的引用。当 int a = 0; 不执行时,代码虽然已经离开了 placeholder 的作用域,但是后续并没有任何对局部变量表的读写操作,placeholder 原本所占用的 Slot 还没有被其他变量所复用,所以 placeholder 作为 GC Roots(所有 Java 线程当前活跃的栈帧里指向 Java 堆里的对象的引用) 仍然是可达对象。当 int a = 0; 执行时,placeholder 的 Slot 被变量 a 复用,所以 GC 触发时,placeholder 变成了不可达对象,即可被 GC 回收。

2.2.2 操作数栈(Operand Stack)

  • 操作数栈是一个后入先出(Last In First Out)栈,方法的执行操作在操作数栈中完成,每一个字节码指令往操作数栈进行写入和提取的过程,就是入栈和出栈的过程。

  • 同局部变量表一样,操作数栈的最大深度也是Java 程序编译成 Class 文件时被写入到 Class 文件格式属性表的 Code 属性的 max_stacks 数据项中。

  • 操作数栈的每一个元素可以是任意的 Java 数据类型,32位数据类型所占的栈容量为1,64位数据类型所占的栈容量为2,在方法执行的任何时候,操作数栈的深度都不会超过在 max_stacks 数据项中设定的最大值(指的是进入操作数栈的 “同一批操作” 的数据类型的栈容量的和)。

  • 当一个方法刚刚执行的时候,这个方法的操作数栈是空的,在方法执行的过程中,通过一些字节码指令从局部变量表或者对象实例字段中复制常量或者变量值到操作数栈中,也提供一些指令向操作数栈中写入和提取值,及结果入栈,也用于存放调用方法需要的参数及接受方法返回的结果。例如,整数加法的字节码指令 iadd(使用 iadd 指令时,相加的两个元素也必须是 int 型) 在运行的时候将操作数栈中最接近栈顶的两个 int 数值元素出栈相加,然后将相加结果入栈。

2.2.3 动态连接(Dynamic Linking)

  • 每个栈帧都包含一个指向**运行时常量池(JVM 运行时数据区域)**中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态链接。

  • <span style='color:red'>在 Class 文件格式的常量池(存储字面量和符号引用)中存有大量的符号引用(1.类的全限定名,2.字段名和描述符,3.方法名和描述符),字节码中的方法调用指令就以常量池中指向方法的符号引用为参数。这些符号引用一部分会在类加载过程的解析阶段的时候转化为直接引用(指向目标的指针、相对偏移量或者是一个能够直接定位到目标的句柄),这种转化称为静态解析。另外一部分将在每一次的运行期期间转化为直接引用,这部分称为动态连接。</span>

    看看以下代码的 Class 文件格式的常量池:

// java 代码
 public Test test() { return new Test(); } // 字节码指令 // Class文件的常量池 Constant pool: #1 = Methodref #4.#19 // java/lang/Object."<init>":()V #2 = Fieldref #3.#20 // com/alibaba/uc/Test.i:I #3 = Class #21 // com/alibaba/uc/Test #4 = Class #22 // java/lang/Object #5 = Utf8 i #6 = Utf8 I #7 = Utf8 <init> #8 = Utf8 ()V #9 = Utf8 Code #10 = Utf8 LineNumberTable #11 = Utf8 LocalVariableTable #12 = Utf8 this #13 = Utf8 Lcom/alibaba/uc/Test; #14 = Utf8 test #15 = Utf8 ()I #16 = Utf8 <clinit> #17 = Utf8 SourceFile #18 = Utf8 Test.java #19 = NameAndType #7:#8 // "<init>":()V #20 = NameAndType #5:#6 // i:I #21 = Utf8 com/alibaba/uc/Test #22 = Utf8 java/lang/Object public int test(); descriptor: ()I flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: getstatic #2 // Field i:I 3: areturn LineNumberTable: line 8: 0 LocalVariableTable: Start Length Slot Name Signature 0 4 0 this Lcom/alibaba/uc/Test; 

从上面字节码指令看出 0: getstatic #2 // Field i:I 这行字节码指令指向 Constant pool 中的 #2,而 #2 中指向了 #3 和 #20 为符号引用,在类加载过程的解析阶段会被转化为直接引用(指向方法区的指针)。

2.2.4 方法返回地址

  • 当一个方法开始执行后,只有两种方式可以退出这个方法。第一种方式是执行引擎遇到任意一个方法返回的字节码指令(例如:areturn),这时候可能会有返回值传递给上层的方法调用者(调用当前方法的方法称为调用者),是否有返回值和返回值的类型将根据遇到何种方法返回指令来决定,这种退出方法的方式称为正常完成出口(Normal Method Invocation Completion)。
  • 另外一种退出方式是,在方法执行过程中遇到了异常,并且这个异常没有在方法体内得到处理,无论是Java虚拟机内部产生的异常,还是代码中使用 athrow 字节码指令产生的异常,只要在本方法的异常处理器表中没有搜索到匹配的异常处理器,就会导致方法退出,这种退出方法的方式称为异常完成出口(Abrupt Method Invocation Completion)。一个方法使用异常完成出口的方式退出,是不会给它的上层调用者产生任何返回值的。
  • 无论采用何种退出方式,在方法退出之后,都需要返回到方法被调用的位置,程序才能继续执行,方法返回时可能需要在栈帧中保存一些信息,用来帮助恢复它的上层方法的执行状态。一般来说,方法正常退出时,调用者的程序计数器的值可以作为返回地址,栈帧中很可能会保存这个计数器值。而方法异常退出时,返回地址是要通过异常处理器表来确定的,栈帧中一般不会保存这部分信息。
  • 方法退出的过程实际上就等同于把当前栈帧出栈,因此退出时可能执行的操作有:恢复上层方法的局部变量表和操作数栈,把返回值(如果有的话)压入调用者栈帧的操作数栈中,调整程序计数器的值以指向方法调用指令后面的一条指令等。

2.3 栈内存溢出

  • 栈帧过多导致栈内存溢出

    例如递归方法,当方法调用层级过多,产生大量的栈帧,却没有出栈,就会导致栈内存溢出,抛出StackOverflowError异常

    示例代码

    public class Demo {
        private static int count; public static void main(String[] args) { try { method1(); } catch (Throwable e) { e.printStackTrace(); System.out.println(count); } } private static void method1() { count++; method1(); } } 

    执行结果

    java.lang.StackOverflowError
    	at com.esell.Demo.method1(Demo.java:22)
    	at com.esell.Demo.method1(Demo.java:22)
    	at com.esell.Demo.method1(Demo.java:22)
    	at com.esell.Demo.method1(Demo.java:22)
    	at com.esell.Demo.method1(Demo.java:22)
    	at com.esell.Demo.method1(Demo.java:22)
    	at com.esell.Demo.method1(Demo.java:22)
    	at com.esell.Demo.method1(Demo.java:22)
    14602
    

    可以看到方法总共执行了14602次就导致了栈溢出,可以在虚拟机运行参数中设置-Xss128k调整栈内存大小,结果执行次数就会变小

  • 如果 Java 虚拟机栈可以动态扩展,并且在尝试扩展的时候无法申请到足够的内存,或者在创建新的线程时没有足够的内存去创建对应的虚拟机栈,那 Java 虚拟机将抛出一个 OutOfMemoryError 异常

2.4 线程运行诊断

2.4.1 cpu占用过高

示例代码

public class Demo {

    public static void main(String[] args) { new Thread(null, () -> { System.out.println("1..."); while(true) { } }, "thread1").start(); new Thread(null, () -> { System.out.println("2..."); try { Thread.sleep(1000000L); } catch (InterruptedException e) { e.printStackTrace(); } }, "thread2").start(); new Thread(null, () -> { System.out.println("3..."); try { Thread.sleep(1000000L); } catch (InterruptedException e) { e.printStackTrace(); } }, "thread3").start(); } } 

linux上运行此代码

javac Demo.java
nohup java Demo &

查看输出

tail -f nohup.out
1...
2...
3...
1...
2...
3...

查看cpu状况

top

可以看到刚才运行的java程序占用cpu很高,进程号为10526

查询该进程下所有线程的运行状态

top -Hp 10526

可以看到占用cpu最高的线程是10536

使用jstack工具获取10526进程中所有线程运行信息

jstack 10526
2019-12-17 17:24:00
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.11-b03 mixed mode):

"Attach Listener" #12 daemon prio=9 os_prio=0 tid=0x00007f94d0001000 nid=0x294a waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"DestroyJavaVM" #11 prio=5 os_prio=0 tid=0x00007f94f8008800 nid=0x291f waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"thread3" #10 prio=5 os_prio=0 tid=0x00007f94f80e7000 nid=0x292a waiting on condition [0x00007f94fd9e3000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
	at java.lang.Thread.sleep(Native Method)
	at Demo.lambda$main$2(Demo.java:24)
	at Demo$$Lambda$3/1523554304.run(Unknown Source)
	at java.lang.Thread.run(Thread.java:745)

"thread2" #9 prio=5 os_prio=0 tid=0x00007f94f80e5000 nid=0x2929 waiting on condition [0x00007f94fdae4000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
	at java.lang.Thread.sleep(Native Method)
	at Demo.lambda$main$1(Demo.java:15)
	at Demo$$Lambda$2/1072591677.run(Unknown Source)
	at java.lang.Thread.run(Thread.java:745)

"thread1" #8 prio=5 os_prio=0 tid=0x00007f94f80e3800 nid=0x2928 runnable [0x00007f94fdbe5000]
   java.lang.Thread.State: RUNNABLE
	at Demo.lambda$main$0(Demo.java:6)
	at Demo$$Lambda$1/640070680.run(Unknown Source)
	at java.lang.Thread.run(Thread.java:745)

"Service Thread" #7 daemon prio=9 os_prio=0 tid=0x00007f94f80a8800 nid=0x2926 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C1 CompilerThread1" #6 daemon prio=9 os_prio=0 tid=0x00007f94f80a5800 nid=0x2925 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread0" #5 daemon prio=9 os_prio=0 tid=0x00007f94f80a3000 nid=0x2924 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Signal Dispatcher" #4 daemon prio=9 os_prio=0 tid=0x00007f94f80a1000 nid=0x2923 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Finalizer" #3 daemon prio=8 os_prio=0 tid=0x00007f94f8071800 nid=0x2922 in Object.wait() [0x00007f94fe1eb000]
   java.lang.Thread.State: WAITING (on object monitor)
	at java.lang.Object.wait(Native Method)
	- waiting on <0x00000000e3520e78> (a java.lang.ref.ReferenceQueue$Lock)
	at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:142)
	- locked <0x00000000e3520e78> (a java.lang.ref.ReferenceQueue$Lock)
	at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:158)
	at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)

"Reference Handler" #2 daemon prio=10 os_prio=0 tid=0x00007f94f806d800 nid=0x2921 in Object.wait() [0x00007f94fe2ec000]
   java.lang.Thread.State: WAITING (on object monitor)
	at java.lang.Object.wait(Native Method)
	- waiting on <0x00000000e3521030> (a java.lang.ref.Reference$Lock)
	at java.lang.Object.wait(Object.java:502)
	at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:157)
	- locked <0x00000000e3521030> (a java.lang.ref.Reference$Lock)

"VM Thread" os_prio=0 tid=0x00007f94f8068800 nid=0x2920 runnable 

"VM Periodic Task Thread" os_prio=0 tid=0x00007f94f80ad800 nid=0x2927 waiting on condition 

JNI global references: 152

其中thread1thread2thread3是我们自己创建的线程,其他都是jvm的线程

获取占用cpu最高的线程号的十六进制

printf '%x\n' 10536
2928

匹配jstack得到的线程信息

"thread1" #8 prio=5 os_prio=0 tid=0x00007f94f80e3800 nid=0x2928 runnable [0x00007f94fdbe5000]
   java.lang.Thread.State: RUNNABLE
	at Demo.lambda$main$0(Demo.java:6)
	at Demo$$Lambda$1/640070680.run(Unknown Source)
	at java.lang.Thread.run(Thread.java:745)

最后,匹配代码的第6行,得出占用cpu过高的原因的在线程中无限循环执行导致。

2.4.2 程序执行很长时间没有结果

示例代码

public class Demo {
    static A a = new A(); static B b = new B(); public static void main(String[] args) throws InterruptedException { new Thread(()->{ synchronized (a) { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (b) { System.out.println("我获得了 a 和 b"); } } }).start(); Thread.sleep(1000); new Thread(()->{ synchronized (b) { synchronized (a) { System.out.println("我获得了 a 和 b"); } } }).start(www.shicaiyl.com); } } class A { } class B { } 

linux运行此代码

javac Demo.java
nohup java Demo &

进程号为10633,查看输出时,发现一直没有反应

根据进程号获取所有线程信息

jstack 10633
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.11-b03 mixed mode):

"Attach Listener" #11 daemon prio=9 os_prio=0 tid=0x00007fc3a8001000 nid=0x299f waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"DestroyJavaVM" #10 prio=5 os_prio=0 tid=0x00007fc3d0008800 nid=0x298a waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Thread-1" #9 prio=5 os_prio=0 tid=0x00007fc3d00dd800 nid=0x2994 waiting for monitor entry [0x00007fc3ad8d2000]
   java.lang.Thread.State: BLOCKED (on object monitor)
	at Demo.lambda$main$1(Demo.java:21)
	- waiting to lock <0x00000000e3539580> (www.dajuhezc.cn a A)
	- locked <0x00000000e3539590> (a B)
	at Demo$$Lambda$2/1072591677.run(Unknown Source)
	at java.lang.Thread.run(Thread.java:745)

"Thread-0" #8 prio=5 os_prio=0 tid=0x00007fc3d00db800 nid=0x2993 waiting for monitor entry [0x00007fc3ad9d3000]
   java.lang.Thread.State: BLOCKED (on object monitor)
	at Demo.lambda$main$0(Demo.java:13)
	- waiting to lock <0x00000000e3539590> (a B)
	- locked <0x00000000e3539580> (a A)
	at Demo$$Lambda$1/640070680.run(Unknown Source)
	at java.lang.Thread.run(Thread.java:745)

"Service Thread" #7 daemon prio=9 os_prio=0 tid=0x00007fc3d00a8800 nid=0x2991 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C1 CompilerThread1" #6 daemon prio=9 www.shentuylzc.cn_prio=0 tid=0x00007fc3d00a5800 nid=0x2990 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread0" #5 daemon prio=9 os_prio=0 tid=0x00007fc3d00a3000 nid=0x298f waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Signal Dispatcher" #4 daemon prio=9 www.shentuylzc.cn_prio=0 tid=0x00007fc3d00a1000 nid=0x298e runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Finalizer" #3 daemon prio=8 os_prio=0 tid=0x00007fc3d0071800 nid=0x298d in Object.wait() [0x00007fc3c05fc000]
   java.lang.Thread.State: WAITING (on object monitor)
	at java.lang.Object.wait(Native Method)
	- waiting on <0x00000000e3520e78> (a java.lang.ref.ReferenceQueue$Lock)
	at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:142)
	- locked <0x00000000e3520e78> (a java.lang.ref.ReferenceQueue$Lock)
	at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:158)
	at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)

"Reference Handler" #2 daemon prio=10 os_prio=0 tid=0x00007fc3d006d800 nid=0x298c in Object.wait() [0x00007fc3c06fd000]
   java.lang.Thread.State: WAITING (on object monitor)
	at java.lang.Object.wait(Native Method)
	- waiting on <0x00000000e3521030> (a java.lang.ref.Reference$Lock)
	at java.lang.Object.wait(Object.java:502)
	at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:157)
	- locked <0x00000000e3521030> (a java.lang.ref.Reference$Lock)

"VM Thread" os_prio=0 tid=0x00007fc3d0068800 nid=0x298b runnable 

"VM Periodic Task Thread" os_www.jiuhuaylgw.cn prio=0 tid=0x00007fc3d00ad800 nid=0x2992 waiting on condition 

JNI global references: 151


Found one Java-level deadlock:
=============================
"Thread-1":
  waiting to lock monitor 0x00007fc3b4003778 (www.huizhonggjpt.cn object 0x00000000e3539580, a A),
  which is held by "Thread-0"
"Thread-0":
  waiting to lock monitor 0x00007fc3b40062c8 (object 0x00000000e3539590, a B),
  which is held by "Thread-1"

Java stack information for the threads listed above:
===================================================
"Thread-1":
	at Demo.lambda$main$1(Demo.www.qiaoheibpt.com java:21)
	- waiting to lock <0x00000000e3539580> www.feishenbo.cn(a A)
	- locked <0x00000000e3539590> (a B)
	at Demo$$Lambda$2/1072591677.run(Unknown Source)
	at java.lang.Thread.run(Thread.java:745)
"Thread-0":
	at Demo.lambda$main$0(Demo.java:13)
	- waiting to lock <0x00000000e3539590> (a B)
	- locked <0x00000000e3539580> (a A)
	at Demo$$Lambda$1/640070680.run(Unknown Source)
	at java.lang.Thread.run(Thread.www.baihua178.cn java:745)

Found 1 deadlock.

Found one Java-level deadlock可以在底部看到这句话,意思是死锁,根据后面具体信息可知,在代码29行,Thread-1锁住对象b,在等待对象a的锁,而在代码13行,Thread-0锁住对象a,在等待对象b的锁,从而造成死锁,程序无反应。

Guess you like

Origin www.cnblogs.com/laobeipai/p/12111129.html