JVM内存划分

来源:互联网 发布:淘宝返现卡片 编辑:程序博客网 时间:2024/06/05 20:13

简述

Java虚拟机内存整体上划分为5个部分(本地方法栈、Java虚拟机栈、程序计数器、堆、方法区)。这五个部分只是Java虚拟机规范的固定,但是Java虚拟机并不唯一,而是多个机构甚至多个企业都有不同的实现方式,因此不同内存区域的具体实现并不是固定的,这个约定于具体的应用场景。

本地方法栈

本地方法栈和java栈的原理和作用是非常相似的。区别是值java栈是为执行java方法执行服务的。本地方法栈是执行为native方法服务的。

Java虚拟机栈

也叫虚拟机栈,平常所说的栈即是此栈。栈的生命周期决定于线程,每一个线程都会创建自己的栈,栈由多个栈帧构成,每一次方法调用都会创建一个栈帧,栈帧中存放局部变量、操作数栈、运行时常量池引用、方法返回地址、附加信息等。栈的核心在于程序的逻辑,相对于栈的堆则重在数据的存放。

局部变量表:存储方法中的局部变量,局部变量分基本数据类型和引用数据类型,针对基本数据类型的变量,存储的是值(每种基本数据类型都有固定的内存占用大小),引用数据类型的变量存储的是对象的引用。局部变量表的大小,在编译器时期就可以确定,所以程序执行期间,局部变量表的大小是不会发生改变的
操作数栈:线程执行方法的过程,实际上就是执行语句的过程,归根到底就是计算机计算的过程。然而程序中所有的计算都是借住于操作数栈来完成的(计算过程牵扯到中间变量和中间值,当前的理解这些数据存放于此)。可以理解成,它就是用来做计算的。
运行时常量池的引用:方法执行过程中可能会用到类中的常量,必须要有一个引用指向运行时常量。
方法返回地址:方法执行完成后,要返回之前调用它的地方,因此栈帧中必须保存方法的返回地址。
附加信息:java虚拟机规范允许一些虚拟机的具体实现增加一些规范中没有描述的信息到栈帧中,例如与调试相关的信息,这部分信息完全取决于虚拟机的具体实现。

程序计数器

Java具有多线程特性,在JVM中,多线程是轮流获取cpu执行权的,所以,每个线程都会有自己的程序计数器,并相互独立。在JVM规范中,如果当前程序执行的不是native方法(本地方法),程序计数器中的值应该是下一条指令的地址,如果是native方法,程序计数器中的值应该是undefined。程序计数器中存储的数据占用的空间,不会随程序的执行发生改变,所以程序计数器不会发生内存溢出现象(存放的仅仅是地址)。

Java中的堆是用来存储对象本身和数组的(数组的指针在java栈中)。
堆这部分空间是java垃圾回收的主要区域。
堆是被所有线程共享的,在JVM中只有一个堆。
 
JDK8中的堆内分划分和JDK7是不一样的,JDK7以及之前的版本 PermGen Space 是在堆中的,当永久代满了之后会报 OutOfMemoryError:PermGen Space 错误。JDK8中永久代就不在堆中了,而是移到了本地内存中,会根据永久代对象的大小动态调整metaSpace区的大小。JDK8也提供了设置metaspace大小的参数,-XX:MaxMetaspaceSize=128M 。如果不设置JVM将会根据一定的策略自动增加本地元内存空间。如果设置的元内存空间过小,你的应用程序可能得到以下错误:OutOfMemoryError:metadata space.
 

Eden:S0:S1=8:1:1.

Eden: 
对象创建,最初是在这个内存池中分配内存的。
当eden区满的时候,触发minorGC,minorGC逐一检查eden区的对象是否有引用,没有引用的对象被清理掉。

Survivor1(s1):
MinorGC后,从Eden区活下来的对象被移动到survivor space(幸存区),当MinorGC再次扫描的时候,也会扫描survivor space中的对象,检查时候有引用,没有被清理,有就会被标记存货次数(经过了几次minorGC)。
 
Survivor1(s2):
和survivor(s1)是一样的作用和原理,之所以要分1和2是为了保证堆中对象是在一个连续的内存中,防止内存的碎片化,提升程序效率。
S1和S2还有一个名字from 和to ,s1和s2交替作为from和to。
当s1是from的时候s2就是to。当s1是to的时候,s2就是from。(哪个幸存区是空的哪个就是to,反之有对象的幸存区叫from)
每次minorGC清理完没有了引用的对象,都会把数据从from转移到to中(转移到to中的连续内存中),通过这种方式,防止了堆内存的碎片化。
 
Old:
当一个对象经过了多次的minorGC后,依旧存活(默认15次GC,这个次数可配置),该对象将被转移到Old区(老年代)。

方法区

从Java8开始,方法区被删除,开始使用metaSpace,并且metaSpace不再放在运行时数据区,而是放到了native memory,虽然以后再也看不到OOME:PermGen space 了,但是多了一个OOME:mata space 。
 
它和堆一样是所有线程共享的。
存放了一些常量、静态变量、类信息等,可以理解成class文件在内存中的存放位置。
 
静态常量池(类,方法的内存模型):
所谓静态常量池,就是*.class中的常量池(每个class文件都有一个常量池),class文件中不仅包含代码中所定义的各种基本类型(如int、long等等)和对象型(如String及数组)的常量值外,还包含一些以文本形式出现的符号引用,比如:
  类和接口的全限定名;
  字段的名称和描述符;
方法的名称和描述符。
 
运行时常量池:
JVM完成类装载操作后,将n个class文件中的常量池载入到内存中,并保存在方法区中。我们常说的常量池,就是指方法区中的运行时常量池。


原创粉丝点击