文章目录

  • 一.JDK整体体系架构
  • 二.JVM虚拟机
  • 栈帧
  • 程序计数器
  • Minor GC
  • Full GC
  • 方法区
  • 本地方法栈




一.JDK整体体系架构

java 虚拟机栈占用内存 jvm 虚拟机栈里面有什么_jvm

在不同的系统中,java代码能够做到一次编写、到处运行,其原因就是因为不同系统的java虚拟机(JVM)能够解析各个地方编写java代码。



二.JVM虚拟机

java 虚拟机栈占用内存 jvm 虚拟机栈里面有什么_jvm_02

  • 堆、方法区是共享区域,调优也是调这一部分。
  • 栈、本地方法栈、程序计数器是线程私有的,由JVM自动化管理。
  • PS:对象的存储:对象的实例存储在堆空间,对象的元数据存储在方法区(元空间),对象的引用存储在栈空间


  • 全称为线程栈或者虚拟机栈,其特点是先进后出(FILO),主要存放程序在运行过程中产生的局部变量,不存放成员变量、对象。
  • 栈内存大小,在其编译时就已经确定。


栈帧

java 虚拟机栈占用内存 jvm 虚拟机栈里面有什么_栈_03


main()方法首先启动,方法进栈(压栈),而后执行a()方法,也进行压栈。在a()执行完成后,该方法出栈,而后main()出栈。



  • 一个栈内有多个栈帧,随方法调用而创建,随方法结束而死亡。一个方法,对应着一个栈帧。
  • 局部变量表:是一组变量值的存放空间,用于存放方法参数、局部变量。
  • 操作数栈:虚拟机将操作数栈作为运算工作区域,大多数指令需要在这里执行运算,然后把结果重新压回操作数栈。
  • 动态链接:如果将一个.class文件进行javap -v反汇编,那么可以看到一个常量池
......
    
Constant pool:
   #1 = Methodref          #5.#25         // java/lang/Object."<init>":()V
   #2 = Class              #26            // Demo
   #3 = Methodref          #2.#25         // Demo."<init>":()V
   #4 = Methodref          #2.#27         // Demo.a:()I
   #5 = Class              #28            // java/lang/Object
   #6 = Utf8               <init>
   #7 = Utf8               ()V
   #8 = Utf8               Code
   #9 = Utf8               LineNumberTable
  #10 = Utf8               LocalVariableTable
       
......

java源文件被编译的字节码(.class)文件中,所有的变量和方法引用都作为符号引用存储在常量池中,以减少数据量;在调用方法时,就是通过常量池中指向的方法的符号引用来表示。而动态链接,就是将符号引用,转化为直接引用。

  • 方法出口:在方法执行完毕后,返回之前调用它的地方。但出现异常,则不会返回地址。


程序计数器

每一个线程都会有一个独立的程序计数器,而其作用简单来说就是当前线程所执行的字节码的行号指示器

比如

1)有这么一个程序如下

public class Demo
{
    public static void main(String[] args)
    {
        Demo demo = new Demo();
        demo.a();
    }


    public int a()
    {
        int a = 1;
        int b = 2;
        int c = a + b;
        return c;
    }
}



2)javap命令可以将class文件分解、反编译,还可以查看java编译器生成的字节码等。

这时候使用javap -c demo.class进行编译,对代码进行反汇编;

每一句话都有对应的意义,可以通过JVM指令手册进行对照

Compiled from "Demo.java"
public class Demo {
  public Demo();
    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 Demo
       3: dup
       4: invokespecial #3                  // Method "<init>":()V
       7: astore_1
       8: aload_1
       9: invokevirtual #4                  // Method a:()I
      12: pop
      13: return

  public int a();
    Code:
       0: iconst_1
       1: istore_1
       2: iconst_2
       3: istore_2
       4: iload_1
       5: iload_2
       6: iadd
       7: istore_3
       8: iload_3
       9: ireturn
}

3)可以看到,每个方法中,语句都对应相应的行号,而程序计数器,可以存放当前所执行的地址(行号)



java 虚拟机栈占用内存 jvm 虚拟机栈里面有什么_java 虚拟机栈占用内存_04

  • jdk1.8后,永生代已经被正式移除。
  • 堆的内存分配,新生代 + 幸存者区约占1/3,老年代约2/3。而在1/3中,新生代又占了其中约4/5,幸存者区共占1/5。


Minor GC
  1. 在Eden中内存满了以后,JVM执行引擎会进行垃圾收集,对其进行清理。这时,一些变量已经没有作用(比如栈帧中的一些方法中new出来的局部变量已经出栈、被释放,那么堆中的对象没有了指针指向它,成为了游离的状态,成为了无效对象),此时就会被回收;而没有被回收的对象,就会存入Survivor区。
  2. 对象有一个对象头,其中有一个分带年龄,当从Eden挪到Survivor时,分带年龄就会+1。
  3. 在Survivor区中,也会进行minor gc,当一个区域进行minor gc后对象仍然存在,那么就会将其分带年龄+1,并且将对象移动到另一个区域中;两个Survivor交替minor gc、对象交替在二者中轮转。
  4. 当对象的分带年龄到达了15时,此对象就会被移动到老年代中。


Full GC
  1. Full GC是清理整个堆空间,包括所有区域。


方法区

  • 又称元空间、Non-Heap(非堆),它直接使用内存,而不使用JVM中的内存空间。
  • 所有定义的方法的信息都保存在该区域,存储已被虚拟机加载的类信息、常量、静态变量、运行时常量池等数据。
  • 它是所有线程的共享区域,所以线程也被设计成安全的。


本地方法栈

本地方法栈具有线程隔离的特点,但是它服务的对象是JVM的native方法,比如调用C/C++库。