Understanding the JVM - java 内存区
2017-10-11 本文已影响35人
Lin_Shao
java内存区域
- 程序计数器
程序计数器存放线程当前执行的字节码的地址,字节码解释器可以通过改变计数器的值来完成顺序执行、分支、循环、跳转、异常处理、线程恢复等基础功能。
如果线程正在执行一个native方法,计数器的值为undefined。 - 虚拟机栈
当线程调用一个java方法时,会创建对应的栈帧,用来存放该方法的相关数据。每个方法从调用到执行完成的过程,对应着一个栈帧从创建、入栈到出栈的过程- 局部变量表
由若干个Slot组成,长度由编译期决定。
单个Slot可以存储一个类型为boolean,byte,char,short,float,reference,returnAddress(方法的返回地址,即调用该方法的地址)的数据,两个Slot可以存储一个类型为long或double的数据。
局部变量表用于方法间参数传递,以及方法执行过程中存储基础数据类型的值和对象的引用。 - 操作数栈
由若干个Entry组成,长度由编译期决定
单个Entry即可以存储一个Java虚拟机中定义的任意数据类型的值,包括long和double类型,但是存储long和double类型的Entry深度为2,其他类型的深度为1
在方法执行过程中,栈帧用于存储计算参数和计算结果;在方法调用时,操作数栈也用来准备调用方法的参数以及接收方法返回结果
- 局部变量表
- 本地方法栈
本地方法栈的作用与虚拟机栈及其相似,只不过对应的栈帧存放的是native方法的信息。java虚拟机规范并没有对其做出强制规定,所以其实现由具体的虚拟机决定 - java堆
java head是jvm所管理的内存中最大的一块,是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存的区域的唯一目的就是用来存放对象实例,几乎所有的对象实例和数组都要在这里分配内存(JIT编译器、逃逸分析技术、栈上分配、标量替换技术会导致对象不需要在堆上分配内存)。
java堆是垃圾回收器管理的主要区域。
java堆的大小可以通过-Xms或-Xmx控制 - 方法区
在一个jvm实例的内部,类型信息被存储在一个称为方法区的内存逻辑区中。类型信息是由类加载器在类加载时从类文件中提取出来的。类(静态)变量也存储在方法区中。
因为方法区是被所有线程共享的,所以必须考虑数据的线程安全。假如两个线程都在试图找lava的类,在lava类还没有被加载的情况下,只应该有一个线程去加载,而另一个线程等待。
方法区的大小不必是固定的,jvm可以根据应用的需要动态调整。
jvm可以允许用户和程序指定方法区的初始大小,最小和最大尺寸。
方法区同样存在垃圾收集,因为通过用户定义的类加载器可以动态扩展程序,一些类也会成为垃圾。
jvm可以回收一个未被引用类所占的空间,以使方法区的空间最小。- 运行时常量池
运行时常量池是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项就是常量池,用来存放编译期间生成的各种字面量和符号引用。这部分内容将在来加载后进入方法区的运行时常量池中存放。
运行时常量池相对于Class文件常量池的另一个重要特性就是动态性。Java语言并不要求常量一定只有编译期才能进入,也并非只有Class文件的常量才能进入,运行时也可能将新的常量放入池中。这种特性呗被开发人员运用的最多的就是String的intern()方法。
- 运行时常量池
- 直接内存
直接内容并不是jvm所控制的内容区域,而是通过native方法向本地宿主动态申请的一块内容区域。
在JDK1.4中新加入了NIO类,引入了一种基于通道(channel)与缓冲区(Buffer)的I/O,它可以使用Native函数分堆外内容,然后通过一个存储在java堆中的DirectByteBuffer对象作为这块内容的引用进行操作。这样能避免在java堆和native内容中反复交换数据,在某些场景中能显著提高性能。