目录
一、程序计数器(PC Register)
程序计数器是用于存放下一条指令所在单元的地址的地方。
特点:线程私有,不会有内存溢出的情况。
可通过字节码指令文件看到每个指令的编号,如:
源码:
package jvm;
class MainTest {
public static void main(String[] args) {
Junior junior = new Junior();
func1();
}
private static void func1(){
func2(12,34);
}
private static int func2(int num1,int num2) {
int c = num2 + num1;
return c;
}
}
class Junior{
}
javac编译,生成class文件:
D:\javaTest\javaT\src\jvm>javac MainTest.java
javap反汇编器查看字节码内容
D:\javaTest\javaT\src\jvm>javap -c MainTest.class
Compiled from "MainTest.java"
class jvm.MainTest {
jvm.MainTest();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: new #2 // class jvm/Junior
3: dup
4: invokespecial #3 // Method jvm/Junior."<init>":()V
7: astore_1
8: invokestatic #4 // Method func1:()V
11: return
}
二、虚拟机栈(JVM Stack)
1.概念与特点
-
虚拟机栈线程运行需要的内存空间,由一个个栈帧组成(对应着每次方法调用时所占用的内存),每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法
-
方法调用结束自动跳出栈,不需要垃圾回收处理
-
-Xss1m 指定栈内存为1M,默认1024k(windows除外)
2.StackOverFlowError
StackOverFlowError 栈内存溢出的情况:
- 栈帧过多,如无限递归调用
- 栈帧过大
以下为-Xss512k的运行结果:
3.idea设置jvm运行参数
4.linux下的java线程诊断
top命令查看java进程id与cpu使用率
ps命令查看线程id与对应的cpu和mem占用
[root@localhost lzq]# ps H -eo pid,tid,%cpu,%mem | grep 1832 | grep -v 0.0
1832 4739 13.9 9.7
1832 4740 0.1 9.7
1832 4915 0.7 9.7
1832 4955 0.1 9.7
1832 5073 0.7 9.7
……
可以看出线程id为4739的线程占用CPU最高,计算4739的16进制为0x1283
jstack pid 命令查看java进程中每个线程的信息,找到线程id为1283的线程查看具体信息
[root@localhost lzq]# jstack 1832
…省略部分输出…
"Thread-2" #22 daemon prio=5 os_prio=0 tid=0x000000001a748800 nid=0x1283 waiting on condition [0x000000001846f000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000000c5b95198> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2045)
at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1081)
at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:809)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
…省略输出…
三、本地方法栈(Native Method Stack)
本地方法栈为:JVM调用本地方法时提供的内存空间,本地方法一般为c或C++实现,Object类下很多方法都是native方法:
四、堆(Heap)
1.概念与特点
- 定义:通过new关键字创建对象都会使用堆内存
- 特点:线程共有,需考虑线程安全问题,有垃圾回收机制
- 堆内存参数:-Xmx,默认-Xmx4g
2.堆内存溢出
执行如下代码
package jvm;
import java.util.ArrayList;
import java.util.List;
class MainTest {
public static void main(String[] args) {
int i = 0;
List list =null;
try{
list = new ArrayList();
String s = "StringA...";
while (true) {
list.add(s);
s+=s;
i++;
}
}catch (Throwable e) {
e.printStackTrace();
System.out.println(i);
}
}
}
结果(默认-Xmx4g):
java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3332)
at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:124)
at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:448)
at java.lang.StringBuilder.append(StringBuilder.java:136)
at jvm.MainTest.main(MainTest.java:15)
26
-Xmx4m后:
java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOfRange(Arrays.java:3664)
at java.lang.String.<init>(String.java:207)
at java.lang.StringBuilder.toString(StringBuilder.java:407)
at jvm.MainTest.main(MainTest.java:15)
14
3.堆内存诊断(jps,jmap,jconsole)
测试代码
package com.my.jvm;
public class HeapTest {
final static int _10MB = 1024 * 1024 * 10;
public static void main(String[] args) throws InterruptedException {
System.out.println("1..."); // jmap 查询初始堆内存信息
Thread.sleep(15000);
byte[] bytes = new byte[_10MB];
System.out.println("2...");// new一个10M的数组之后 jmap 查询堆内存信息
Thread.sleep(30000);
bytes = null;
System.gc();
System.out.println("3..."); //gc垃圾回收之后 jmap 查询堆内存信息
Thread.sleep(3000000);
}
}
运行java程序后,jps 命令查询当前运行的java进程
C:\Users\Administrator\IdeaProjects\spring_demo>jps
4884 Launcher
9828
7404 Jps
7436 HeapTest
"1…"后:
C:\Users\Administrator\IdeaProjects\spring_demo>jmap -heap 7436
Attaching to process ID 7436, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.291-b10
using thread-local object allocation.
Parallel GC with 13 thread(s)
Heap Configuration:
MinHeapFreeRatio = 0
MaxHeapFreeRatio = 100
MaxHeapSize = 4273995776 (4076.0MB) # 最大堆内存,即-Xmx参数指定的值
NewSize = 89128960 (85.0MB)
MaxNewSize = 1424490496 (1358.5MB)
OldSize = 179306496 (171.0MB)
NewRatio = 2
SurvivorRatio = 8
MetaspaceSize = 21807104 (20.796875MB)
CompressedClassSpaceSize = 1073741824 (1024.0MB)
MaxMetaspaceSize = 17592186044415 MB
G1HeapRegionSize = 0 (0.0MB)
Heap Usage:
PS Young Generation
Eden Space: # Eden Space 新创建对象使用的空间
capacity = 67108864 (64.0MB)
used = 8057288 (7.684028625488281MB) # 初始7M
free = 59051576 (56.31597137451172MB)
12.00629472732544% used
……
C:\Users\Administrator\IdeaProjects\spring_demo>jmap -heap 7436
……
Eden Space:
capacity = 67108864 (64.0MB)
used = 50000344 (17.684043884277344MB) # 17M
free = 17108520 (16.315956115722656MB)
74.50631856918335% used
……
C:\Users\Administrator\IdeaProjects\spring_demo>jmap -heap 7436
……
Heap Usage:
PS Young Generation
Eden Space:
capacity = 67108864 (64.0MB)
used = 1252016 (1.1940155029296875MB) # 1.2M
free = 65856848 (62.80598449707031MB)
1.8656492233276367% used
……
五、方法区(Method Area)
1.概念
包含类的信息,类加载器信息以及常量池等,默认使用系统内存,参数使用-XX:MaxMetaspaceSize=16m指定元空间内存大小
批量加载大量类的代码
package com.my.jvm;
import jdk.internal.org.objectweb.asm.ClassWriter;
import jdk.internal.org.objectweb.asm.Opcodes;
public class MethodAreaTest extends ClassLoader {
public static void main(String[] args) {
int i = 0;
try {
MethodAreaTest methodAreaTest = new MethodAreaTest();
for (int j = 0; j < 100000; j++, i++) {
// 生成一万个类的字节码对象
ClassWriter classWriter = new ClassWriter(0);
classWriter.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, "Class" + j, null, "java/lang/Object", null);
// 加载上面的一万个类
byte[] bytes = classWriter.toByteArray();
methodAreaTest.defineClass("Class" + j, bytes, 0, bytes.length);
}
} finally {
System.out.println(i);
}
}
}
根据jdk厂商不同,报错可能为:
15811
Exception in thread "main" java.lang.OutOfMemoryError: Compressed class space
也可能为
15811
Exception in thread "main" java.lang.OutOfMemoryError: Metaspace
2.查看常量池
(运行时加载到运行时常量池):
源代码MainTest.java:
package jvm;
class MainTest {
public static void main(String[] args) {
String a = "hello";
System.out.println("abc "+a);
}
}
执行:
javac MainTest.java
javap -v MainTest.class
结果:
Classfile /D:/javaTest/javaT/src/jvm/MainTest.class
Last modified 2021-7-30; size 598 bytes
MD5 checksum 62bb3d8381396c883138cb76823f54b0
Compiled from "MainTest.java"
class jvm.MainTest
minor version: 0
major version: 52
flags: ACC_SUPER
Constant pool:
#1 = Methodref #11.#20 // java/lang/Object."<init>":()V
#2 = String #21 // hello
#3 = Fieldref #22.#23 // java/lang/System.out:Ljava/io/PrintStream;
#4 = Class #24 // java/lang/StringBuilder
#5 = Methodref #4.#20 // java/lang/StringBuilder."<init>":()V
#6 = String #25 // abc
#7 = Methodref #4.#26 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#8 = Methodref #4.#27 // java/lang/StringBuilder.toString:()Ljava/lang/String;
#9 = Methodref #28.#29 // java/io/PrintStream.println:(Ljava/lang/String;)V
#10 = Class #30 // jvm/MainTest
#11 = Class #31 // java/lang/Object
#12 = Utf8 <init>
#13 = Utf8 ()V
#14 = Utf8 Code
#15 = Utf8 LineNumberTable
#16 = Utf8 main
#17 = Utf8 ([Ljava/lang/String;)V
#18 = Utf8 SourceFile
#19 = Utf8 MainTest.java
#20 = NameAndType #12:#13 // "<init>":()V
#21 = Utf8 hello
#22 = Class #32 // java/lang/System
#23 = NameAndType #33:#34 // out:Ljava/io/PrintStream;
#24 = Utf8 java/lang/StringBuilder
#25 = Utf8 abc
#26 = NameAndType #35:#36 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#27 = NameAndType #37:#38 // toString:()Ljava/lang/String;
#28 = Class #39 // java/io/PrintStream
#29 = NameAndType #40:#41 // println:(Ljava/lang/String;)V
#30 = Utf8 jvm/MainTest
#31 = Utf8 java/lang/Object
#32 = Utf8 java/lang/System
#33 = Utf8 out
#34 = Utf8 Ljava/io/PrintStream;
#35 = Utf8 append
#36 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder;
#37 = Utf8 toString
#38 = Utf8 ()Ljava/lang/String;
#39 = Utf8 java/io/PrintStream
#40 = Utf8 println
#41 = Utf8 (Ljava/lang/String;)V
{
jvm.MainTest();
descriptor: ()V
flags:
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 4: 0
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=2, args_size=1
0: ldc #2 // String hello
2: astore_1
3: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
6: new #4 // class java/lang/StringBuilder
9: dup
10: invokespecial #5 // Method java/lang/StringBuilder."<init>":()V
13: ldc #6 // String abc
15: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
18: aload_1
19: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
22: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
25: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
28: return
LineNumberTable:
line 6: 0
line 7: 3
line 8: 28
}
SourceFile: "MainTest.java"