java jvm 基础
1.jvm执行java程序时内存的区域划分
1.1程序计数器:简称PC (可以参考下面方法区class字节码的行号)
1.1.1 作用
在java虚拟机中所有java代码要编译成class字节码文件,由字节码解释器通过改变程序计数器的值来选取下一个需要执行的字节码指令,分支、循环、跳转,异常处理、线程恢复等基础功能。
1.1.2 生命周期
每个线程私有,确保了线程独立,native方法没有对应的程序控制器的值。
1.1.3 个人理解
有了它我们就知道下面开始执行哪一条语句,相当于保存了每个执行语句的位置,并且记录了执行的状态否则也就谈不上线程恢复的功能了。
1.2 java虚拟机栈
1.2.1 作用
用来描述方法执行的内存模型,每个方法的执行都会创建一个栈帧,用来存储局部变量、操作数栈、动态链接、方法出口等信息。每个方法的执行是入栈道出栈的过程。栈帧是随着方法的创建而创建,随着方法的结束而销毁,如果方法抛出异常,也算方法结束。然而在每一个栈帧中,都有着自己的局部变量表以及操作数栈以及对当前类的运行时常量池的引用。
1.2.2 局部变量
存放编译期间的基本类型(int,short,long等)和对象引用类型但是不是对象本身(可能是指向对象的起始位置或对象的句柄或其他与对象有关的位置)。long,double占用俩个局部变量空间其他则占用1个。局部变量所占的内存空间要在编译期间完成分配,当进入一个方法的时,这个方法需要在帧中分配多大的局部变量空间是确定的,运行期间不会改变局部变量表的大小。
1.2.3操作数栈
它是一个后进先出(LIFO)栈,而它的长度也是在编译时期就写入了class文件当中,是固定的。它的作用就是提供字节码指令操作变量计算的空间,比如简单的,对于int a=9这句话来说,就需要先将9压入操作数栈,再将9赋给a这个变量。和局部变量区一样,操作数栈也是被组织成一个以字长为单位的数组。但是和前者不同的是,它不是通过索引来访问,而是通过标准的栈操作—压栈和出栈—来访问的。比如,如果某个指令把一个值压入到操作数栈中,稍后另一个指令就可以弹出这个值来使用。
1.2.4*Error
栈请求深度大雨虚拟机允许的深度则会抛出
public class Test1 { private static void ces (){ ces(); System.out.println("栈深度测试"); } public static void main (String args []){ //不停循环调用方法也就是一直在压栈 ces(); } }
1.2.5 OutOfMemoryError(-Xss设定栈容量)
Java 虚拟机栈允许动态扩展,也允许固定长度,如果拓展时申请不到足够的内存那么就会抛出改异常。OutOfMemoryError 是堆和栈异常总称,所以局部变量区域内存溢出和堆内存溢出都会出现OutOfMemoryError错误
1.2.6 生命周期 (线程私有)
1.3 本地方法栈 : 同java虚拟机栈 只不过执行的是native方法
1.4 java 堆(java heap 俗称GC堆 -Xms -Xmx设置参数)
1.4.1 作用
java虚拟机管理内存实例的地方,几乎所有实例都在这里分配。它随着JAVA虚拟机的启动创建,储存着所有对象实例以及数组对象,而且内置了“自动内存管理系统”,也就是我们常说的垃圾搜集器。JAVA堆中的内存释放是不受开发人员控制的,完全由JAVA虚拟机一手操办。对于JAVA虚拟机如何实现垃圾搜集器,JAVA虚拟机规范没有明确的规定,也正因如此,我们平时使用的JAVA虚拟机中提供了许多种垃圾搜集器,它们采用不同的算法以及实现方式,已满足多方面的性能需求。
1.4.2 生命周期:所有线程共享区域
1.4.3 事例
// -Xms10m -Xmx10m import java.util.List; /** * Created by coffice on 2017/3/1. */ public class Test1 { private static void ces (){ ces(); System.out.println("栈深度测试"); } public static void main (String args []){ List<String> list = new ArrayList<String>(); while (true){ list.add("测试"); System.out.println("list添加成功"); } } } //由于list一直添加字符串对象导致堆内存溢出 Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
1.5 方法区
1.5.1 描述
java虚拟机规范将它描述为堆的一个逻辑部分,但是显然还是有区别的因此他有个别名Non-Heap非堆就是为了区分java堆
1.5.2 作用
存储已经加载的类的信息(方法,字段,接口等)、常量、静态变量、即时编译后的代码等数据(如果你加载了足够多的类超出java堆的容量那么就会OutOfMemoryError了)
1.5.3 生命周期:所有线程共享
1.5.4 字节码
public class Test1 { // private static void ces (){ // ces(); // System.out.println("栈深度测试"); // } // public static void main (String args []){ // List<String> list = new ArrayList<String>(); // while (true){ // list.add("测试"); // System.out.println("list添加成功"); // } // } public static void main (String args []){ List<String> list = new ArrayList<String>(); while (true){ list.add("测试"); System.out.println("list添加成功"); } } } cofficedeMacBook-Pro:java coffice$ javap -c com/coffice/improve/jvm/Test1.class Compiled from "Test1.java" public class com.coffice.improve.jvm.Test1 { public com.coffice.improve.jvm.Test1(); 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 java/util/ArrayList 3: dup 4: invokespecial #3 // Method java/util/ArrayList."<init>":()V 7: astore_1 8: aload_1 9: ldc #4 // String 测试 11: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z 16: pop 17: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream; 20: ldc #7 // String list添加成功 22: invokevirtual #8 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 25: goto 8 }
1.6 运行时常量池(属于方法区一部分)
1.6.1 作用
方法区一部分,存放类编译期间生成的各种字面量和符号引用,这些会在类加载后进入方法区运行的常量池中
1.6.1 事例
//“1”会被存储到常量池中 String s = new String("1");
1.7 直接内存(也会导致OutOfMemoryError)
1.7.1 作用
虚拟机通过使用Native函数直接分配堆外内存,然后通过一个存储在java堆中的DirectByteBuffer对象来 作为这块内存的引用操作。
1.7.2 生命周期
不属于java虚拟机是NIO通过Native函数直接分配的堆外内存。
下一篇: IOS 极光推送