java内存区域浅析

前段时间重新拿起《深入理解java虚拟机》这本书,以前每一次都是翻翻就过来,没有对内容进行总结和记录。一方面,为了强化自己对于知识的理解和记忆;另一方面,希望能通过自己的总结帮助那些想要了解这一方面知识的童鞋。本系列会分几个篇章进行介绍,由于本人技术水平有限,只是浅析,望能与大家一起成长。

概述:

大多数程序员常常会听到内存优化这个词,尤其是从事Android等移动平台开发的工作者,由于硬件基础有限,需要在软件上进行内存等各种优化。java程序员通常对内存的了解或是掌控能力是无法于c或c++程序员相比,因为java提供虚拟机去管理内存,而c程序可以灵活地对应用的内存进行控制。因此,多数java程序员对于内存管理知识是偏薄弱。

java运行时内存区域

Java虚拟机运行时数据区

程序计数器

程序计数器是一块相对较小的区域,它存储的是字节码的指令地址,也就是当前线程所执行的字节码的行号指示器。java程序编译后会生成对应的字节码文件,程序在运行时java虚拟机执行的是字节码文件。java虚拟机的多线程实现,其实是通过不断地轮流切换线程并分配处理器执行时间的方式实现的,在任何一个时间点上,处理器只执行一个线程的指令(单核情况)。为了解决线程不断切换后,能够继续上一次执行的位置,就需要在每一个线程中保存其字节码指令的地址。因此,程序计数器是线程私有区域,不能被共享。程序计数器是java虚拟机规范中唯一一个不会发生OOM的区域。

虚拟机栈

初入门学习java的童鞋肯定会有堆区、栈区的概念,很多人把java运行时数据区域直接划分为堆区、栈区。这种划分其实是片面的,虽然这两个区域是比较常接触到,以及主要的区域,但是这种理解是错误的。虚拟机栈也是线程私有区域,它的生命周期与线程相同。

虚拟机栈描述的是java方法执行的内存模型:每个方法在执行时都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每个方法从开始到结束,就对应一个栈帧在虚拟机栈中的入栈与出栈操作。虚拟机栈中核心区域就是局部变量表

局部变量表中存放的是方法中定义的各种基本数据类型(boolean、int、short等等)以及对象的引用类型(不是对象本身,指向对象的指针或句柄)。

所有数据类型除了64位的long和double类型会占用2个局部变量空间(Slot)之外,其余的都只是占用一个1个。局部变量表所需要的空间分配通常在编译期间完成,当方法执行时,局部变量表的大小已经是完全确定,不会被改变。

虚拟机站在运行时可能会出现2种异常,StackOverflowError(栈溢出)和OOM。

本地方法栈

本地方法栈与虚拟机栈类似,都是线程私有的区域。不同的在于,两者服务的对象不同。虚拟机栈服务的是java方法,存储其相关的数据。本地方法栈服务于Native层的方法,存储Native方法中的数据信息。

堆区

java的堆区(Heap)是内存中最大的一块区域,所有线程共享使用。堆区主要存放的是对象,所有线程创建的对象都会在堆区。java堆区也是GC操作的主要区域,从线程角度来看,虚拟机可以划分给每个线程一块私有的分配缓冲区(Thread Local Allocation Buffer, TALB)。无论怎么划分,堆区还是用来存放对象。

方法区

方法区与java堆区一样,都是各个线程共享的内存区域,其主要用于存储类信息、常量、静态变量、即时编译器编译后的代码等数据

直接内存

直接内存并不是虚拟机运行时数据区的一部分,它是在Native层的函数库直接分配的堆外内存。JDK1.4版本引入的NIO(异步IO),其底层就是使用的直接内存。直接内存在读写上速度比虚拟机内存会快,不用拷贝数据到虚拟机,对于性能提升很有帮助。

结束语

本篇文章主要是对java运行时的数据区域进行介绍,让大家对内存有个初步理解,在撸代码时知道数据都对应在什么区域。后续文章会继续介绍虚拟机内存的其他知识。强烈安利一波《深入理解java虚拟机》,很值得一读。

猜你喜欢

转载自blog.csdn.net/hb_csu/article/details/80700004