JVM--java虚拟机内存结构及各结构详解

一、程序计数器(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"

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

运维小菜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值