jvm

Posted dazhaung

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了jvm相关的知识,希望对你有一定的参考价值。

自动内存管理

运行时数据区

运行时数据区主要包括虚拟机栈、本地方法栈、程序计数器、方法区、堆等,其中方法区和堆属于线程共享内存区域;虚拟机栈和本地方法栈属于线程隔离的数据区

技术图片

程序计数器

占用内存空间较小,可以当作是当前线程执行的字节码行号指示器。jvm的多线程是通过线程切换并分配一定处理时间的方式来实现的,所以多个线程在运行时候,当前线程退出cpu时,必须有个东西记住当前线程执行的java方法的虚拟机字节码指令的地址。如果当前正在执行的方法是本地方法(native),计数器值为空。程序计数器不会导致oom

java虚拟机栈

java虚拟机栈也是属于线程私有的一块内存区域。生命周期和线程相同;java虚拟机栈描述的时java方法执行时候的内存模型:每个方法的运行时候会创建一个栈帧,用处储存局部变量表、操作数栈、方法出口、动态链接等信息。每个方法的调用到执行完毕,对应的就是这个栈帧的入栈到出栈的过程;

局部变量表:用于储存对象的引用(可能是对象在堆中的内存地址,也可能是一个该对象的句柄的地址,编译期可知的各种基本数据类型,其中long和double各占两个字节,别的都时一个字节。局部变量表在栈帧中的空间大小是进入方法时候就已经完全确定了,而且中间不会改变。

操作数栈: 一个方法的调用执行其实就是方法调用时创建一个栈帧,然后再栈帧内部会根据类的信息获取指令,从局部变量表中取数据,进行操作数栈的入栈出栈方式执行指令。

技术图片

java堆

java堆是虚拟机管理的最大的一块内存区域,在虚拟机启动的时候分配空间;java堆存在的主要作用是存放对象实例;他也是垃圾收集器主要管理的一块内存区域;从垃圾回收的角度来看,可以将java堆分为新生代和老年代,细一点可以分为eden区,to Survivor区,from Survivor区等。从内存分配的角度,java堆可能划分出来多个线程私有的内存分配缓冲区( Thread Local Allocation Buffer)简称TLAB;我们系统运行中出现的oom最多就是出现在java堆中。

方法区

? 方法区和java堆一样,是一个线程共享的内存区域,它用于储存已经被虚拟机加载的类的信息、常量、静态变量、即时编译器编译后的代码等数据。java虚拟机规范把他描述成堆的一个逻辑部分,但是他和咱们所说的java堆还是区分开的。很多人把方法区称为永久代,他和真正意义上的永久代还是有区别的,本质上来说两者并不等价,只不过是我们的虚拟机设计团队为了少些一些代码,把GC分代收集算法扩展到了方法区而已,这样就可以像管理堆内存一样管理我们的方法区。但是同样,这样更容易造成的我们的oom的问题(原因有点模糊,有待研究,后期补上去)。后期java虚拟机会逐步改善方法区,例如,jdk1.7已经把原本放在永久代中的字符串常量池移出来了。

? 方法区和Java堆一样不需要连续的内存和可以选择固定大小或者可扩展外,还可以选择不进行垃圾回收!!java虚拟机对这块的区域回收成绩不太理想。主要是针对常量池的回收和类型的卸载,但是类型的卸载条件比较苛刻。首先必须没有对象引用该类;没有地方使用该类的反射方法,也没有累的class对象。

? 需要注意的是,我们有时候在代码中会用到大量的cglib动态代理的时候,有可能会类过多引起针对方法区的垃圾回收

运行时常量池

? 运行时常量池是方法区的一部分,我们的class文件除了有类的版本、字段、方法、接口等信息以外,还有一项信息就是运行时常量池,用于存放编译期生成的各种字面量和符号引用,这部分内容在类加载后进入方法区的运行时常量池中存放

? 运行时常量池具有动态性,并不是只有编译器产生的常量才能进入方法区的运行时常量池,运行期间也是可以的,比如我们的String.intern()方法

直接内存

? 直接内存不是运行时数据区的一部分,也不是java虚拟机规范定义的内存区域,但也会被频繁的使用

我们平时使用的NIO,是一种基于通道和缓冲区的I/O方式,它可以使用native函数库直接分配堆外内存,然后通过一个存在java堆中的DirectByteBuffer对象作为这块内存的引用进行操作。

? 直接内存并不属于运行时数据区,自然也不会受到java堆大小的限制,我们在分配内存的时候,往往忽略了这块内存区域大小的分配,导致分配总内存大于物理内存,导致oom的发生

HotSpot 虚拟机对象

? 我们写代码的时候经常会new 一个对象,但是我们new一个对象的时候,就行在我们的内存中发生了什么,我们new出来的对象究竟是个什么样子的?

对象的创建

? 当我们的虚拟机检测到new指令的时候,我们的虚拟机会拿着这个指令的参数去常量池中定位一个类的符号的引用,然后检查这个符号代表的类有没有被加载、解析和初始化过,如果没有,就执行一遍这个过程

以上是关于jvm的主要内容,如果未能解决你的问题,请参考以下文章

jvm基础--JVM参数配置

jvm基础--JVM内存模型

jvm基础--JVM内存模型

JVM基础:深入学习JVM堆与JVM栈(转)

JVM堆与JVM栈

JVM内存管理和JVM垃圾回收机制