欢迎访问悦橙教程(wld5.com),关注java教程。悦橙教程  java问答|  每日更新
页面导航 : > > 文章正文

【Java基础随机】Java内存区域,

来源: javaer 分享于  点击 10469 次 点评:222

【Java基础随机】Java内存区域,


Java内存区域

总体结构图

各部分区域

一、程序计数器(寄存器)
二、JAVA虚拟机栈
  • 局部标量表是一组变量值的存储空间,用于存放 方法参数 和 局部变量。在Class 文件的方法表的 Code 属性的 max_locals 指定了该方法所需局部变量表的最大容量。
  • 操作数栈中的每一个元素可以是任意的Java数据类型,方法刚刚开始执行的时候,这个方法的操作数栈是空的,方法执行过程中,会有各种字节码指令往操作数栈中写入和提取内容,如算术运算时通过操作数栈来进行。如整数加法的字节码指令iadd在运行的时候操作数栈中最接近栈顶的两个元素已经存入了两个int型的数值,当执行这个指令时,会将这两个Int值出栈并相加,然后将相加的结果入栈。其栈中元素数据类型与字节码指令的序列时严格匹配的,不可能出现long和float使用iadd命令相加的情况。
  • 每个栈帧都包含一个执行运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接。Class 文件中存放了大量的符号引用,字节码中的方法调用指令就是以常量池中指向方法的符号引用作为参数。这些符号引用一部分会在类加载阶段或第一次使用时转化为直接引用,这种转化称为静态解析。另一部分将在每一次运行期间转化为直接引用,这部分称为动态连接。
  • 方法正常退出时,调用者的PC计数器的值可以作为返回地址,栈帧中很可能保存这个计数器值。异常处理器表确定返回地址时,栈帧一半不会保存这部分信息。
三、本地方法栈
四、Java堆
  • Eden Space字面意思是伊甸园,对象被创建的时候首先放到这个区域,进行垃圾回收后,不能被回收的对象被放入到空的survivor区域。
  • Survivor Space幸存者区,用于保存在eden space内存区域中经过垃圾回收后没有被回收的对象。Survivor有两个,分别为To Survivor、 From Survivor,这个两个区域的空间大小是一样的。执行垃圾回收的时候Eden区域不能被回收的对象被放入到空的survivor(也就是To Survivor,同时Eden区域的内存会在垃圾回收的过程中全部释放),另一个survivor(即From Survivor)里不能被回收的对象也会被放入这个survivor(即To Survivor),然后To Survivor 和 From Survivor的标记会互换,始终保证一个survivor是空的。
  • Eden Space和Survivor Space都属于新生代,新生代中执行的垃圾回收被称之为Minor GC(因为是对新生代进行垃圾回收,所以又被称为Young GC),每一次Young GC后留下来的对象age加1。(虚拟机给每个对象定义了一个对象年龄计数器,当从Eden成功存活至To Survivor时,age=1,在Survivor空间中每“熬过”一次Minor GC,年龄就增加一岁,当年龄达到设置的参数标准时[默认15岁],对象就会被晋升到老年代中)。
  • Old Gen老年代,用于存放新生代中经过多次垃圾回收仍然存活的对象,也有可能是新生代分配不了内存的大对象会直接进入老年代。经过多次垃圾回收都没有被回收的对象,这些对象的年代已经足够old了,就会放入到老年代。当老年代被放满的之后,虚拟机会进行垃圾回收,称之为Major GC。由于Major GC除并发GC外均需对整个堆进行扫描和回收,因此又称为Full GC。
  • Code Cache代码缓存区,它主要用于存放JIT所编译的代码。CodeCache代码缓冲区的大小在client模式下默认最大是32m,在server模式下默认是48m,这个值也是可以设置的,它所对应的JVM参数为ReservedCodeCacheSize 和 InitialCodeCacheSize,可以设置其值。
  • Perm Gen全称是Permanent Generation space,是指内存的永久保存区域,因而称之为永久代。这个内存区域用于存放Class和Meta的信息,Class在被 Load的时候被放入这个区域。因为Perm里存储的东西永远不会被JVM垃圾回收的,所以如果你的应用程序LOAD很多CLASS的话,就很可能出现PermGen space错误。默认大小为物理内存的1/64。
  1. 这里,提一下Java堆的内存分配机制。
    1)、对象优先在Eden分配
    大多数情况下,对象在新生代Eden区中分配,当Eden区中没有足够空间进行分配时,虚拟机会发动一次Minor GC。
    2)、大对象直接进入老年代
    大对象指的是需要大量连续内存空间的Java对象,最典型的大对象就是很长的字符串以及数组(如大的byte[])。大对象对虚拟机的内存分配来说是一个不好的消息,经常导致内存还有不少空间的时候就提前出发垃圾收集以获取足够的连续空间来“安置”它们。虚拟机会提供一个可设置的参数,令大于设置值的对象直接在老年代分配。
    3)、长期存活的对象将进入老年代
    对应虚拟机分代收集的思想管理内存,在垃圾收集内存回收时就必须能识别哪些对象应该放在新生代,哪些对象应该放在老年代中。为做到这一点,虚拟机给对象定义了一个对象年龄计数器,当从Eden成功存活至To Survivor时,age=1,在Survivor空间中每“熬过”一次Minor GC,年龄就增加一岁,当年龄达到设置的参数标准时[默认15岁],对象就会被晋升到老年代中。对象晋升老年代的年龄阈值,可以通过参数设置。
    4)、动态对象年龄判定
    虚拟机并不是永远的要求对象的年龄必须打到晋升老年代的年龄阈值才能晋升老年代,如果Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无需等到对象的年龄计数器达到晋升老年代的年龄阈值的要求。

  2. 根据Java虚拟机规范,Java堆可以处于在物理上不连续的内存空间,但是只要逻辑上连续就可以了。它既可以固定大小,也可以扩展,现在主流虚拟机都是按照可扩展来实现的,如果堆中没有内存完成实例分配,且无法扩展的时候,将会抛出内存溢出异常。

  3. 关于垃圾回收,可以查看具体关于垃圾回收的博客。

  4. 附Java虚拟机规范中的原文:The heap is the runtime data area from which memory for all class instances and arrays is allocated.,即堆是为所有类实例和数组分配内存的运行时数据区域。

五、方法区
  • 若你定义了一个 String s = new String(“xyz”); 你觉得在内存中分配了几个对象?
  • 答案是1个或2个,如果常量池中原来没有 ”xyz”, 就是两个。如果原来的常量池中存在“xyz”时,就是一个。但是s指向的地址是堆中的对象。因为它是通过new String()来实例的。若你使用String s = “xyz”; 来定义s的话,首先在常量池中查找是否存在内容为"xyz"字符串对象,如果不存在则在常量池中创建"xyz",并让str引用该对象,如果存在则直接让str引用该对象。

相关文章

    暂无相关文章
相关栏目:

用户点评