目录

一、JVM运行时数据区

1、程序计数器

2、虚拟机栈(stack)

    1)、局部变量

    2)、操作数栈

    3)、动态链接

    4)、出口

3、本地方法栈(Native Method Stack)

4、方法区

5、堆(Heap)

6、直接内存


一、JVM运行时数据区

        Java程序运行时,会基于类加载机制将类文件加载到JVM,并将类中的信息分解后放置到JVM的各个模块,以提供运行。程序(Java类)由数据、指令和控制(return等)组成。下面是JVM运行时数据区:

1、程序计数器

        当前线程正在执行Java方法时,计数器存储的当前方法正在执行的字节码指令的地址和行号;若当前线程正在执行Native方法时,计数器存储的值为Undefined。并且程序计数器是唯一一个不会涉及OutOfMemoryError的区域。

2、虚拟机栈(stack)

        虚拟机栈用于存储当前线程正在运行方法的数据、指令和返回地址。我们大多数情况下关注的是虚拟机栈中的局部变量表,局部变量表所需的内存控制的编译器已经确定,当进入一个方法时,所需的局部变量槽(Slot)已经确定(double和long占用两个槽)。

        当线程请求的栈深度大于虚拟机运行的最大深度,则会抛出StackOverflowError;虚拟机栈动态扩展时(现在大部分Java虚拟机的虚拟机栈都支持动态扩容,少部分不支持),若无法申请到足够的内存空间,则会抛出OutOfMemoryError

    1)、局部变量

        局部变量分为基本类型和引用类型:基本类型直接进行存储,并且long和double类型会存储两个局部变量空间(Slot);引用类型会则:1、指向Heap中的指针、对象的句柄或者位置 2、returnAddress地址(返回地址类型)。内存空间一经过分配就不会再进行改变,包括运行期间不能进行调整。

    2)、操作数栈

       出栈和入栈

    3)、动态链接

       常量池中查找父类引用指向子类对象的真实实例对象。

    4)、出口

        return、try catch 等控制程序

3、本地方法栈(Native Method Stack)

        本地方法栈除了处理对象(native方法)不同,其他与虚拟机栈一致,并且在HotSpot中直接将两个进行合并,所以该部分也可能包StackOverflowError和OutOfMemoryError(原因与虚拟机栈相同)。

4、方法区

    方法区与永久代是不完全等同的,但是很多时候为了方便理解,基本都理解为一致。方法区与堆一样:可以是非连续的内存空间,可以设置孤独的内存大小或者设置可以扩展的内存大小,可以不实现垃圾回收器。

        1)、类信息    存放类的版本号、字段、方法等信息

        2)、常量(常量池)

    常量池并不是在编译器存储之后后续就不会动态扩展的区域,比如在运行期间String的intern()方法可以将常量放入常量池中,所以也会存在OutOfMemoryError问题。

        3)、静态变量

        4)、JIT(just-in-time ) 动态代理时候动态生成的代码

5、堆(Heap)

    Java堆(Heap)方法区是线程共享的,跟随Java虚拟机的生命周期。Java的一切都是对象,所有对象实例及数组都会分配在堆中(JIT、逃逸分析除外)。堆在G1垃圾回收器之前,都是按分代进行收集,可以理论上将堆分为新生代、老年代、永久代,Eden区、From Survivor空间、To Survivor空间等。Java堆的划分主要是为了更快的分配对象(new的时候)、和对使用完的对象的内存进行回收。从内存分配角度,Java的线程共享区可以分配出多个Java线程的私有缓存空间TLAB(Thread Local Allocation Buffer)

    Java虚拟机可以通过(-Xms、-Xmx)控制初始化内存和最大内存的大小,但是当在分配对象时(分配对象规则则是后面的内容)空间不够,并且不能再进行扩展时,则会在该区域抛出OutOfMemoryError异常。

6、直接内存

    直接内存并不是运行时数据区的一部分,但是也是Java操作内存的一种方式。JDK4中引入New Input/Output类,可以使用Native函数直接分配堆外内存,目的是可以不用在本地方法栈和虚拟机栈中复制数据。虽然,不会受到JVM堆初始化和最大内存设置的限制,但是会受限于本机内存的限制,若堆内存分配空间过大,该部分没有内存可以用,也会照成OutOfMemoryError

 

二、运行时数据区与内存溢出异常

    由上面可知,运行时数据区与内存溢出、栈异常的关系如下:

 

 

 

三、JVM内存模型(JMM)

   

    由于对象的生命周期是不一样的,所以需要对JMM进行分代。JMM分为新生代、老年代和永久代(在JDK1.8将永久代替换为Meta Space,一个可以无限扩展的内存空间),并且新生代与老年代为1:2,因为需要做内存担保。新生代为分eden区、S0区(from Survivor)、S1(to Servivor)区并且比例为8:1:1。根据计算得知,98%的对象会在minor gc的时候会被回收掉。

 

 

四、JVM常用配置参数

   1、jvm配置

    XX比X的稳定性更差,并且版本更新不会进行通知和说明。

1、-Xms   s为strating,表示堆内存起始大小

2、-Xmx   x为max,表示最大的堆内存

3、-Xmn  n为new,表示新生代大小

4、-XX:SurvivorRator=8    表示堆内存中新生代、老年代和永久代的比为8:1:1

5、-XX:PretenureSizeThreshold=3145728   表示当创建(new)的对象大于3M的时候直接进入老年代

6、-XX:MaxTenuringThreshold=15              表示当对象的存活的年龄(minor gc一次加1)大于多少时,进入老年代

7、-XX:-DisableExplicirGC    表示是否(+表示是,-表示否)打开GC日志

 

    2、对象进入老年代的条件

 

1、-XX:PretenureSizeThreshold=3145728   表示当创建(new)的对象大于3M的时候直接进入老年代

2、-XX:MaxTenuringThreshold=15              表示当对象的存活的年龄(minor gc一次加1)大于多少时,进入老年代

3、当同年龄的所有对象的大小总和 > Survivor空间的一半 ,则也会进入老年代

 

 

 

 

 

 

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐