面试必备——java虚拟机内存模型

JDK,JRE,JVM关系如图所示

 

 

从图中就可以很清晰的看清他们之间的关系:JDK>JRE>JVM

 

JAVA程序的运行(为什么java可以跨平台)

比如我们编写一个HelloWord.java ,他是如何运行的呢

                                            javac                                            java    

因为有JVM,所以我们在不同平台只需要下载对应的JDK即可,不同的操作系统的虚拟机实现针对同一份代码会生成不同的适应各自平台的机器码

 

JVM组成

 

 

 

 

方法区,java栈,本地方法栈,堆和程序计数器统称为运行时数据区(jvm内存模型)。

其中对于方法区,很多人更愿意称为:“永久代(Permanent Generation)”,不过本质上两者并不等价,仅仅是因为习惯使用HotSpot虚拟机的设计团队选择吧GC分代收集扩展至方法区,或者说使用永久代来实现方法区而已,这样HotSpot的垃圾收集器就可以像管理Java堆一样管理这部分内存,能够省去专门为方法区变编写内存管理代码的工作。不过对于其他虚拟机(如BEA JRockit、IBM J9等)来说并不存在永久代的概念
这是jdk1.8之前的内存模型,其中方法区和堆是是线程共享的,

 JDK 1.7 和 1.8 将字符串常量由永久代转移到堆中,并且 JDK 1.8 中已经不存在永久代的结论。但是在jdk1.8之后
元数据区取代了永久代。
 

元空间

元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制,但可以通过以下参数来指定元空间的大小:

  -XX:MetaspaceSize,初始空间大小,达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整:如果释放了大量的空间,就适当降低该值;如果释放了很少的空间,那么在不超过MaxMetaspaceSize时,适当提高该值。
  -XX:MaxMetaspaceSize,最大空间,默认是没有限制的。

  除了上面两个指定大小的选项以外,还有两个与 GC 相关的属性:
  -XX:MinMetaspaceFreeRatio,在GC之后,最小的Metaspace剩余空间容量的百分比,减少为分配空间所导致的垃圾收集
  -XX:MaxMetaspaceFreeRatio,在GC之后,最大的Metaspace剩余空间容量的百分比,减少为释放空间所导致的垃圾收集

 

程序计数器(线程私有):是当前线程执行字节码的行号计数器,是线程私有的内存区域。当执行的是java方法的话,计数器记录的是虚拟机字节指令(当前指令)的地址,当执行的是native方法时,则为空。

此内存区域是唯一一个在 Java 虚拟机规范中没有规定任何 OutOfMemoryError 情况的区域。程序执行时这个值由字节码执行引擎维护

 

java虚拟机栈(线程私有):线程私有,生命周期和线程一致

每个方法在执行的时候都会创建一个栈帧 ,存贮了局部变量表,操作数栈,动态链接及方法返回地址。如下图

 

其中局部变量表存放了编译器可知的各种基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference类型,它不等同与对象本身,可能是一个指向对象其实地址的引用指针,也可能是指向一个代表对象的句柄或其他与此对象相关的位置) 和returnAddress类型(指向了一条字节码指令的地址)

 局部变量表的存储空间是32位,刚好可以放一个int类型,所以长度为64为的long和double类型的数据会占用2个局部变量空间(Slot),局部变量表的大小在编译器就已经确定了

 在java虚拟机规范中,对java虚拟机栈规定了两种异常状况:如果线程请求的栈深度大于虚拟机所允许的深度,将会抛出StackOverflowError异常;如果虚拟机栈可以动态扩展(当前大部分虚拟机都可以动态扩展,只不过ava虚拟机规范中也允许固定长度的虚拟机栈),扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常
 

 

本地方法栈(线程私有)

本地方法栈和虚拟机栈所发挥的作用非常相似,区别就是:

    java虚拟机栈为虚拟机执行java方法(也就是字节码)服务
    本地方法栈为虚拟机使用到的Native方法服务

也会有 StackOverflowError 和 OutOfMemoryError 异常。

 

java堆(线程共享)

 

对所有线程共享的一块内存区域,在虚拟机启动的时候创建,用于存放对象的实例。

在 Java 中,堆被划分成两个不同的区域:新生代 ( Young )、老年代 ( Old )。新生代 ( Young ) 又被划分为三个区域:Eden、From Survivor、To Survivor,这样划分的目的是为了使 JVM 能够更好的管理堆内存中的对象,包括内存的分配以及回收。java堆是java虚拟机管理的内存中最大的一块,java堆是被所有线程共享的一块内存区域,堆的唯一目的就是存放实例对象,几乎所有的对象实例都在这里分配内存。
 java堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可,就像我们磁盘空间一样。(不过在实现中既可以大小固定,也可以是可扩展,通过-Xmx 和-Xms控制),如果在堆中没有内存完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError异常

 

方法区(线程共享):方法区和堆一样,是各个线程共享的区域,用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码数据等
ps:方法区中还包括运行时常量池(Runtime Constant Pool),Class文件除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池(Constant Pool Tabel),用于存放编译期生成的各种字面量常量和符号引用,这部分内容将在类加载后进入方法区的运行时常量中存放,当常量池无法再申请到内存时也会抛出OutOfMemoryError异常

 

JVM内存参数设定
-Xms 初始堆内存大小
-Xmx 最大堆内存大小
-Xss 单个线程栈大小
-XX:NewSize 初始新生代堆大小
-XX:MaxNewSize 生代最大堆大小
-XX:PermSize 方法区初始大小(JDK1.7及以前)
-XX:MaxPermSize 方法区最大大小(JDK1.7及以前)
-XX:MetaspaceSize 元数据区初始值(JDK1.8)
-XX:MaxMetaspaceSize 元数据区最大值(JDK1.8)
参数设置示例
jdk1.7 windows设置tomcat的catalina.bat
set JAVA_OPTS=-Xms1024m -Xmx1024m -Xss1m -XX:PermSize=128m -XX:MaxPermSize=256m -XX:NewSize=256m -XX:MaxNewSize=256m
jdk1.8 windows设置tomcat的catalina.bat
set JAVA_OPTS=-Xms1024m -Xmx1024m -Xss1m -XX:MetaspaceSize=128m -XX:MAXMetaspaceSize=256m -XX:NewSize=256m -XX:MaxNewSize=256m

jdk1.7 linux设置tomcat的catalina.sh
JAVA_OPTS=-Xms1024m -Xmx1024m -Xss1m -XX:PermSize=128m -XX:MaxPermSize=256m -XX:NewSize=256m -XX:MaxNewSize=256m
jdk1.8 linux设置tomcat的catalina.sh
JAVA_OPTS=-Xms1024m -Xmx1024m -Xss1m -XX:MetaspaceSize=128m -XX:MAXMetaspaceSize=256m -XX:NewSize=256m -XX:MaxNewSize=256m

 

补充:堆和方法区的联系

 

方法区中的静态变量可能是堆中对象的引用

堆中的对象,对象头中存储了指向方法区中的类元信息的指针

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值