JVM内存管理

Java中的体系架构

在Java体系架构中JVM属于jre环境中的一部分。


体系架构.png

JVM执行过程

首先我们先将程序代码编译成class或者jar文件,然后通过ClassLoader将字节码写入JVM的运行时数据区,JVM一边翻译一边执行


jvm机制.png

jvm执行过程.png

JVM运行时数据区

  • 线程共享:所有线程都共用一个
  1. 方法区
    用来存放类的信息(类的字节码),常量,静态变量,
    jdk1.7之前是永久代,存放在虚拟机中
    jdk1.8是元空间
    元空间存放在本地存储中,只会受本地内存的限制

  2. 用来存放new出来的对象以及该对象中的普通成员变量
  • 线程不共享:每个线程独有一份
  1. 程序计数器
    用来记录当前代码执行的位置
    因为线程在运行的过程中会被CPU进行调度去执行其他线程代码,在调度的过程中会对线程的数据进行保存或读取,那下次获取执行权的时候该线程如何知道自己执行到了哪里?就是通过程序计数器来实现的。
  2. 虚拟机栈:每一个方法都是一个栈帧,执行的时候最先执行的先入栈,最后执行的先出栈
//举个例子
public static void main(String[] args){
      a();
}
public static void a(){
      b();
}
public static void b(){
}
虚拟机栈中的栈帧.png

(1) 局部变量表:用来存放方法中的局部变量的
(2) 操作数栈 :对数据进行操作是会进行入栈和出栈
(3) 动态链接 :一般用于多态中寻找某个具体的对象。静态分派,动态分派
(4) 完成出口 :记录方法出栈的位置

  1. 本地方法栈 :native方法的栈,hotspot虚拟机中将虚拟机栈和本地方法栈合二为一了


    JVM运行时数据区模型.png

JVM工作流程

//原始代码
public class StackTest {
    public static final int CONST_NUM=7;
    public static String TAG="StackTest";
    public static void main(String[] args) {
        StackTest stackTest = new StackTest();
        stackTest.add(10,5);
    }
    public int add(int x,int y){
        return x+y;
    }
}
//反编译出来的代码
Compiled from "StackTest.java"
public class com.example.myapplication.stack.StackTest {
  public static final int CONST_NUM;

  public static java.lang.String TAG;

  public com.example.myapplication.stack.StackTest();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #2                  // class com/example/myapplication/stack/StackTest
       3: dup
       4: invokespecial #3                  // Method "":()V
       7: astore_1
       8: aload_1
       9: bipush        10
      11: iconst_5
      12: invokevirtual #4                  // Method add:(II)I
      15: pop
      16: return

  public int add(int, int);
    Code:
       0: iload_1
       1: iload_2
       2: iadd
       3: ireturn

  static {};
    Code:
       0: ldc           #5                  // String StackTest
       2: putstatic     #6                  // Field TAG:Ljava/lang/String;
       5: return
}

虚拟机栈工作过程

虚拟机指令集:https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html
首先看一看add方法是如何执行的,前面我们了解了每一个方法都是一个栈帧,所以add方法会当作一个栈帧压入虚拟机栈中
1.第一步首先应该是将两个参数放入局部变量表中,但是由于虚拟机优化的时候他会在栈帧之间进行数据共享,所以add方法会共享main方法中的局部变量表。

第一步操作.png

2.接下来会执行iload指令,将局部变量表中的角标为1和2的变量加载到操作数栈中
第二步操作.png

3.执行iadd指令,在执行这个指令的时候会将操作数栈中的数据pop,相加之后再放入操作数栈中
第三步操作.png

4.执行ireturn指令将操作数栈中的数据返回

JVM整体过程分析

JVM.png

本地内存

本地内存.png

堆和栈的区别

栈中的数据是线程独享的,而堆中的数据在所有线程中都可以使用
栈中的数据在出了作用域之后就会被释放,但是所有对象都存在堆中,堆需要垃圾回收机制来回收。
栈的大小远小于堆的大小

内存溢出

栈内存溢出:一般情况下都是递归循环调用的时候
堆溢出:空间不够用了,有可能内存泄漏
方法区溢出
(1) 运行时常量池溢出
(2)方法区中保存的Class对象没有被及时回收掉或者Class信息占用的内存超过了我们配置。
本地直接内存溢出:如果本地机器内存不足

你可能感兴趣的:(JVM内存管理)