JVM全称是java Virtual Machine(java虚拟机),JVM屏蔽了与各个计算机平台相关的软件和硬件差异。
在接下来的日子里,通过写博客的形式学习JVM,让自己更懂得Java!
本系列文章是对《深入分析javaweb技术内幕》和《深入理解java虚拟机》的总结,欢迎大家一起吐槽,一起进步。
《JVM解读》第一篇:JVM体系结构
《JVM解读》第二篇:JVM类加载器ClassLoader
人人都知道的java的一大优点就是不需要程序员去显示的分配内存和回收内存,这是由于虚拟机的自动内存管理机制帮我们搞定了这一切。下面我们就来看看虚拟机是如何划分内存的。
我们将java虚拟机划分的内存区域叫做运行时数据区域。因为只有在JVM启动后才会出现这些区域,所以叫做运行时数据区域。先来看张经典的图
程序计数器
程序计数器是一块较小的内存空间,可以看作当前线程所执行的字节码的行号指示器。由于虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器都只会执行一条线程中的指令。为了在线程切换后能恢复到正确的位置,每条线程都需要有一个独立的程序计数器,相互不影响,独立存储,线程私有的。
如果线程正在执行的是一个java方法,这个计数器记录的是正在执行的虚拟机字节码指令地址;如果执行的本地方法,这个计数器为空
此内存区域是唯一一个在java虚拟机规范中没有规定任何OutOfMemoryError情况的区域
虚拟机栈
虚拟机栈也是线程私有的内存区域,栈总是和线程关联在一起的,每当创建一个线程的时候,JVM就会为这个线程创建一个对应的虚拟机栈,这个栈中又包含多个栈帧,栈帧是与方法对应的,每运行一个方法,就会在这个栈中创建一个栈帧,栈帧中存放一些内部变量如方法内定义的变量,方法的入参,返回值等信息。
每当一个方法执行完后,栈帧就会弹出栈帧的元素作为这个方法的返回值,并清除这个栈帧,java栈的栈顶就是当前执行的活动栈,也是正在执行的方法,PC寄存器里的地址也执行此。只有这个活动栈的本地变量可以被操作栈使用,当这个栈帧中又调用另外一个方法时,与之对应的一个新的栈帧又被创建,这个新的栈帧又被放到栈顶,变为活动栈帧,当这个栈帧中所有的指令执行完成后,这个栈帧移除栈,刚才的栈帧又称为活动栈帧,前面的栈帧的返回值又变为这个栈帧的操作栈中一个操作数。如果没有返回值,当前栈帧的操作栈的操作数没有变化。
堆
堆是存储java对象的地方,是JVM管理java对象的核心区域,每个存储在堆中的java对象都是这个对象类的一方副本,它会复制包括继承自父类的所有非静态属性。堆是被所有java线程所共享的,对它的访问要注意同步问题,方法和属性都需要保持一致。
方法区
JVM方法区是用于存储类结构信息的地方,一个class文件被解析成jvm能识别的几个部分,这些不同的部分在这个class被加载到JVM时,会被存储在不同的数据结构中,其中常量池,域,方法数据,方法体,构造函数,类中的专有方法,实例初始化,接口初始化都存在这个区域。
其实方法区也属于堆区,我们通常称为java堆中的永久区(Permanent Generation),这个区域被所有线程共享,这个区域一般在程序启动后的一段时间内就是固定的了,JVM运行一段时间后,需要加载的类通常都已经加载到JVM中了。
方法区的特殊点在于它不像java堆那样会被频繁地被GC回收器回收,它存储的信息比较稳定。但是它存在于java堆中,依然会被GC回收器管理。
运行时常量池
运行时常量池是方法区的一部分,class文件中除了有类的版本,字段,方法等信息外,还有一项就是常量池,用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池。
本地方法栈
本地方法栈是为JVM运行本地方法准备的空间,作用和java栈类似。
以上就是虚拟机运行时数据区的分区情况。下面还要简单介绍下直接内存。直接内存并不是虚拟机运行时数据区的一部分,也不是java虚拟机规范中定义的部分,但是这部分内存也被频繁地使用,同样会导致OutOfMemoryError异常出现。
在java1.4后新加入了NIO(New Input/Output)类,引入了一种基于通道与缓冲区的I/O方法,可以直接使用本地函数库直接分配堆外内存,然后通过一个存储在java堆中的DirectByteBuffer对象最晚这块内存的引用进行操作。可以显著提高性能,因为避免了java堆和Native堆中来回复制数据。
版权声明:本文为博主原创文章,未经博主允许不得转载。
原文地址:http://blog.csdn.net/uxiaolang/article/details/46653765