文章目录
- 一.JDK整体体系架构
- 二.JVM虚拟机
- 栈
- 栈帧
- 程序计数器
- 堆
- Minor GC
- Full GC
- 方法区
- 本地方法栈
一.JDK整体体系架构
在不同的系统中,java代码能够做到一次编写、到处运行,其原因就是因为不同系统的java虚拟机(JVM)能够解析各个地方编写java代码。
二.JVM虚拟机
- 堆、方法区是共享区域,调优也是调这一部分。
- 栈、本地方法栈、程序计数器是线程私有的,由JVM自动化管理。
- PS:对象的存储:对象的实例存储在堆空间,对象的元数据存储在方法区(元空间),对象的引用存储在栈空间。
栈
- 全称为线程栈或者虚拟机栈,其特点是先进后出(FILO),主要存放程序在运行过程中产生的局部变量,不存放成员变量、对象。
- 栈内存大小,在其编译时就已经确定。
栈帧
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)可以看到,每个方法中,语句都对应相应的行号,而程序计数器,可以存放当前所执行的地址(行号)
堆
- jdk1.8后,永生代已经被正式移除。
- 堆的内存分配,新生代 + 幸存者区约占1/3,老年代约2/3。而在1/3中,新生代又占了其中约4/5,幸存者区共占1/5。
Minor GC
- 在Eden中内存满了以后,JVM执行引擎会进行垃圾收集,对其进行清理。这时,一些变量已经没有作用(比如栈帧中的一些方法中new出来的局部变量已经出栈、被释放,那么堆中的对象没有了指针指向它,成为了游离的状态,成为了无效对象),此时就会被回收;而没有被回收的对象,就会存入Survivor区。
- 对象有一个对象头,其中有一个分带年龄,当从Eden挪到Survivor时,分带年龄就会+1。
- 在Survivor区中,也会进行minor gc,当一个区域进行minor gc后对象仍然存在,那么就会将其分带年龄+1,并且将对象移动到另一个区域中;两个Survivor交替minor gc、对象交替在二者中轮转。
- 当对象的分带年龄到达了15时,此对象就会被移动到老年代中。
Full GC
- Full GC是清理整个堆空间,包括所有区域。
方法区
- 又称元空间、Non-Heap(非堆),它直接使用内存,而不使用JVM中的内存空间。
- 所有定义的方法的信息都保存在该区域,存储已被虚拟机加载的类信息、常量、静态变量、运行时常量池等数据。
- 它是所有线程的共享区域,所以线程也被设计成安全的。
本地方法栈
本地方法栈具有线程隔离的特点,但是它服务的对象是JVM的native方法,比如调用C/C++库。