JVM内存格局总结:stack/heap/static/code(技术如果不能换钱,那不过是垃圾而已)

最近一次面试,面试官让我讲讲JVM的内存这一块的理解。我回答的不满意,今天做一个总结。

做一个产品,最终要做到高并发、高可靠。归根结底,是对CPU、内存等资源受限所作出的解决方案。就内存而言,我们写的代码:如new一个对象,都要在内存中来运行,如果内存中没有足够的空间,不能创建新的对象,那么程序就无法运行。所以,在调试程序的过程中我们可以经常看到OutOfMemory之类的错误。在Java中我们知道有垃圾回收机制GC来解决内存泄露的问题,那么它是如何解决的呢?了解这部分知识,我想:1.内存是如何划分的?2.内存是如何管理的?3.GC是如何工作的?(其源码?)4.针对内存问题,技术的发展是怎么一步步的解决这些问题的?(当然有软件、硬件发展两个方面)。

一,先解决内存是如何来划分的?

  在PC中,内存用于暂时存放CPU中的运算数据,以及与硬盘等外部存储器交换数据。我们常用的软件是安装在计算机的硬盘上的,当执行该软件的时候,才把它调用内存来运行。硬盘用于保存:需要永久保存的、大量的数据。内存用于保存:临时的、少量的数据和程序。此刻,我用Office软件,用键盘向电脑敲入文字,文字先被存储在内存中,当选择保存的时候,内存中的数据才会写入磁盘中。

  内存用半导体存储单元制作而成。包括RAM随机存储器、ROM只读存储器、CACHE高速缓存。其中,ROM由于其永久保存、只读不写的个性,我们在ROM中存放计算机的基本程序和数据,如计算机的BIOS选项中的部分。ROM中的部分不受电脑断电的影响,但是RAM中的东西,则会随着电源关闭而数据丢失,我们常购买的内存条就是RAM集成块集中在一起的一小块电路板,RAM中的数据可以读也可以写。缓存这个东西Cache,我们经常听到的名词,比如一级缓存、二级缓存、三级缓存等。缓存是一个位于CPU和内存之间的,一个读写速度比内存更快的存储器。当CPU向内存中写入或读出数据时,这个数据也被存储高速缓冲存储器中。当CPU再次需要这些数据时,CPU就从高速缓冲存储器读取数据,而不是访问较慢的内存,当然,如需要的数据在Cache中没有,CPU会再去读取内存中的数据。

  地址空间,是对物理存储器编码的范围。即是对每一个物理存储单元(一个字节)分配一个号码,通常叫做编址。分配一个号码给一个存储单元的目的是为了找到它,完成数据的读写,这就是所谓的“寻址”。所以地址空间就是寻址空间。我们常说的,我的电脑是内存4G的,就是地址空间是4G,寻址空间是4G,也就是,为每一个字节分配一个号码,总共有4*1024*1024*1024字节。因为KB的含义就是k*Byte.

  知道了上面这些,那么我们接下来要研究的其实就是RAM这一部分了。我们把这一部分知识也常常叫做Java内存的组成。                                                                                                   

我在看《深入理解Java虚拟机--JVM高级特性与最佳实践》,其中第二章中,如上图的彩图所示,java虚拟机所管理的内存将会包括以下几个运行时数据区域。1.程序计数器(Program counter register),指示当前线程所执行的字节码的行号指示器。(程序中的分支、循环、跳转、异常处理、线程恢复都需要依赖这个计数器来完成),如:java虚拟机的多线程是通过线程轮流切换并分配处理器来执行时间的方式来实现的,在任何一个时刻,一个处理器只会执行一个线程中的指令。因此,为了线程切换后能恢复到正确的执行位置,每条线程都需要一个独立的程序计数器,各条线程之间的计数器互不影响,独立存储,我们称这类内存区域为线程私有的内存。如果线程正在执行的是以Java方法,那么这个计数器记录的是正在执行的虚拟机字节码指令的地址。如果执行的是Native方法,那么这个计数器值则为空,此内存区域是唯一一个在java虚拟机规范中没有规定任何outofmemoryerror情况的区域。2.java虚拟机栈,它同程序计数器一样,也是线程私有的。它是一个栈,存放局部变量(包括有:基本数据类型、对象的引用)。栈中的内存空间在编译期间就已经完成分配。在方法的运行期间(思想:运行java程序,其实质就是运行一个main方法),本部分报错会有两种情况:stackOverflowError当线程所请求的栈深度大于虚拟机所允许的栈深度的时候;outOfMemoryError由于虚拟机栈可以做动态的扩展,当扩展无法申请到足够的内存时会抛此异常。(jvm虚拟机规范中,既允许固定长度的虚拟机栈,也允许动态可扩展的虚拟机栈)。3.本地方法栈,类比虚拟机栈。可见他们都是栈。虚拟机栈为虚拟机执行java方法,本地方法栈为虚拟机使用到的Native方法提供服务。4.java堆,堆内存是java虚拟机所管理的内存中最大的一块儿,java堆是被所有线程所共享的一块儿内存区域,在虚拟机启动时创建。它同时也是GC管理最大的一块儿区域。具体的这一部分,根据回收算法的不同分为新生代和老生代。(稍后介绍)5.方法区,在java虚拟机规范中把方法去描述成堆的一个逻辑部分,用于存储:虚拟机加载的类信息、常量、静态变量。6.运行时常量池,它是方法区的一部分。7.直接内存。



java内存分为:堆Heap和非堆Non-Heap区。java虚拟机具有一个堆,堆是运行时数据区域,所有实例和数组的内存均从此处分配。堆是Java虚拟机启动时创建的。在java堆之外的内存称为non-heap memory。JVM主要管理堆和非堆两种类型的内存。堆是留给开发人员使用的。非堆是JVM留给自己使用的,所以方法区、JVM内部处理或者内部优化所需要的内存(JIT编译后的代码缓存)、每个类的结构(运行时常数池、字段、方法数据)以及方法和构造方法的代码都在非堆内存中。

java中内存的分类:1.栈区(stacksegment)由编译器自动分配释放,存放函数的参数值,局部变量的值等,具体方法执行结束之后,系统自动释放JVM的内存资源。(特点:存取速度比堆快,但是存在栈中的数据大小与生存期必须是确定的,没有灵活性。)2.堆区(Heapsegment)由程序员分配和释放,存放由new创建的对象和数组,JVM会不定时的查看这个对象,如果没有引用指向这个对象就回收该对象。(特点:可以动态分配JVM内存大小,生存期也不用事先告诉编译器,因为它是在运行时动态分配JVM内存的;且堆中分配的JVM内存由JAVA虚拟机自动垃圾回收器来管理。)3.静态区(datasegment)存放全局变量、静态变量、字符串常量,不释放。4.代码区(codesegment)用于存放程序中方法的二进制代码,而且是多个对象共享一个代码空间区域。


java中内存的分配:1.寄存器(JVM内部虚拟寄存器,存取速度非常快,程序不可控制,这个一般不用介绍!)2.栈保存局部变量值(包括:基本数据类型;类的实例(堆区对象的引用(指针));)3.堆保存动态产生的数据(new出来的对象(这包含成员变量,不包含成员方法)-->理由:同一个类的对象拥有各自的成员变量,存储在各自的堆中,但是他们共享该类的方法,并不是每创建一个对象就把成员方法复制一次。)4.数据段,存放static定义的静态成员。5.代码段,存放从硬盘上读取的源程序的代码。

基本类型和引用类型。二者作为局部变量,都放在栈中,基本类型直接在栈中保存值,引用类型只保存一个指向堆区的指针,真正的对象在堆里。作为参数时基本类型就直接传值,引用类型传指针。



堆,(java代码中所有的new操作),JVM对堆的初始分配的内存由-Xms指定,默认是物理内存的1/64.JVM给堆最大分配的内存由-Xmx指定,默认是物理内存的1/4.当默认堆内存小于40%的时候,JVM就会增大堆直到-Xmx的最大限制。空余堆内存大于70%时,JVM会减少直到-Xms的最小限制。(有时候,服务器设置-Xms和-Xmx大小相等,目的是避免每次GC后需要调整堆的大小)对象的堆内存由称为垃圾回收器的自动内存管理系统回收。








备注:

1.参考博客1:http://blog.csdn.net/shimiso/article/details/8595564

2.参考博客2:http://blog.csdn.net/java2000_wl/article/details/8009362

3.参考博客3-博客园:http://www.cnblogs.com/springsource/archive/2013/01/11/2856968.html

4.JConsole参考:http://jiajun.iteye.com/blog/810150


 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值