...

Callwoola

大雄'blog

Home 主页 Blog 博客 Tag标签 GitHub开源 about me关于我


java 内存区域 jvm GC 原理

Java GC(Garbage Collection,垃圾收集,垃圾回收)机制,

JVM 历史(Java Virtual Machine)自从1999年的JDK1.2开始直至现在仍在广泛使用的JDK6, 其中默认的虚拟机都是HotSpot。2009年,Oracle收购Sun,加上之前收购的EBA公司,Oracle拥有3大虚拟机中的两个: JRockitHotSpot,Oracle也表明了想要整合两大虚拟机的意图,但是目前在新发布的JDK7中, 默认的虚拟机仍然是HotSpot,因此本文中默认介绍的虚拟机都是HotSpot,相关机制也主要是指HotSpot的GC机制。

Java GC机制主要完成3件事:

经过这么长时间的发展(事实上,在Java语言出现之前,就有GC机制的存在,如Lisp语言), Java GC机制已经日臻完善,几乎可以自动的为我们做绝大多数的事情。 然而,如果我们从事较大型的应用软件开发,曾经出现过内存优化的需求,就必定要研究Java GC机制。

学习Java GC机制,可以帮助我们在日常工作中排查各种内存溢出或泄露问题, 解决性能瓶颈, 达到更高的并发量, 写出更高效的程序。

我们将从4个方面学习Java GC机制,

Java内存区域

了解Java GC机制,必须先清楚在JVM中内存区域的划分。在Java运行时的数据区里,由JVM管理的内存区域分为下图几个模块:

jvm struct

说明

1,程序计数器(Program Counter Register):

程序计数器是一个比较小的内存区域,用于指示当前线程所执行的字节码执行到了第几行, 可以理解为是当前线程的行号指示器。字节码解释器在工作时,会通过改变这个计数器的值来取下一条语句指令。

例如:

程序执行的是一个Java方法,则计数器记录的是正在执行的虚拟机字节码指令地址;

如果正在执行的是一个本地(native,由C语言编写完成) 方法,则计数器的值为Undefined, 由于程序计数器只是记录当前指令地址, 所以不存在内存溢出的情况, 因此,程序计数器也是所有JVM内存区域中唯一一个没有定义

OutOfMemoryError 的区域。

2,虚拟机栈(JVM Stack):

一个线程的每个方法在执行的同时,都会创建一个栈帧(Statck Frame), 栈帧中存储的有局部变量表操作站动态链接、 方法出口等,function 当方法被调用时,栈帧在JVM栈中入栈,当方法执行完成时,栈帧出栈。

每个线程对应着一个虚拟机栈,因此虚拟机栈也是线程私有的。

每个线程对应着一个虚拟机栈,因此虚拟机栈也是线程私有的。

3,本地方法栈(Native Method Statck):

本地方法栈在作用,运行机制,异常类型等方面都与虚拟机栈相同, 唯一的区别是:虚拟机栈是执行Java方法的, 而本地方法栈是用来执行native方法的,在很多虚拟机中(如Sun的JDK默认的HotSpot虚拟机), 会将本地方法栈与虚拟机栈放在一起使用。 本地方法栈也是线程私有的。

4,堆区(Heap):堆区是理解Java GC机制最重要的区域,没有之一。

在JVM所管理的内存中,堆区是最大的一块, 堆区也是Java GC机制所管理的主要内存区域,堆区由所有线程共享, 在虚拟机启动时创建。堆区的存在是为了存储对象实例,原则上讲, 所有的对象都在堆区上分配内存(不过现代技术里,也不是这么绝对的,也有栈上直接分配的)。

5,方法区(Method Area):

在Java虚拟机规范中,将方法区作为堆的一个逻辑部分来对待, 但事实上,方法区并不是堆(Non-Heap);Java GC的分代收集机制分为3个代:青年代,老年代,永久代, 这些作者将方法区定义为“永久代”,这是因为, 对于之前的HotSpot Java虚拟机的实现方式中, 将分代收集的思想扩展到了方法区,并将方法区设计成了永久代。 不过,除HotSpot之外的多数虚拟机,并不将方法区当做永久代,HotSpot本身,也计划取消永久代。

本文中,由于笔者主要使用Oracle JDK6.0,因此仍将使用永久代一词。

方法区在物理上也不需要是连续的,可以选择固定大小或可扩展大小,

并且方法区比堆还多了一个限制:可以选择是否执行垃圾收集。

一般的,方法区上执行的垃圾收集是很少的,这也是方法区被称为永久代的原因之一(HotSpot), 但这也不代表着在方法区上完全没有垃圾收集, 其上的垃圾收集主要是针对常量池的内存回收和对已加载类的卸载。

6,直接内存(Direct Memory):

直接内存并不是JVM管理的内存, 可以这样理解,直接内存,就是JVM以外的机器内存,比如, 你有4G的内存,JVM占用了1G,则其余的3G就是直接内存, JDK中有一种基于通道(Channel)和缓冲区(Buffer)的内存分配方式, 将由C语言实现的native函数库分配在直接内存中, 用存储在JVM堆中的DirectByteBuffer来引用。 由于直接内存收到本机器内存的限制,所以也可能出现OutOfMemoryError的异常。

Java对象的访问方式

一般来说,一个Java的引用访问涉及到3个内存区域:JVM栈,堆,方法区。

以最简单的本地变量引用:Object obj = new Object()为例:

在Java虚拟机规范中,对于通过reference类型引用访问具体对象的方式并未做规定,目前主流的实现方式主要有两种:

jvm struct

JVM 生命周期

以HotSpot为例:

启动
终止
  • 文档信息:
  • 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)
  • 发表日期: 2017-07-1114:46:20+0800
  • 更多内容:
  • Feed订阅:
相关内容:

disqus评论区:

0