分为4个方面来介绍内存分配与回收,分别是内存是如何分配的、哪些内存需要回收、在什么情况下执行回收、如何监控和优化GC机制。

  java GC(Garbage Collction)垃圾回收机制,是java与C/C++的主要区别之一。通过对jvm中内存进行标记,自主回收一些无用的内存。目前使用的最多的是sun公司jdk中的HotSpot,所以本文也以该jvm作为介绍的根本。

  1.Java内存区域

  在java运行时的数据取里,由jvm管理的内存区域分为多个部分:

  程序计数器(program counter register):程序计数器是一个比较校的内存单元,用来表示当前程序运行哪里的一个指示器。由于每个线程都由自己的执行顺序,所以程序计数器是线程私有的,每个线程都要由一个自己的程序计数器来指示自己(线程)下一步要执行哪条指令。

  如果程序执行的是一个java方法,那么计数器记录的是正在执行的虚拟机字节码指令地址;如果正在执行的是一个本地方法(native方法),那么计数器的值为Undefined。由于程序计数器记录的只是当前指令地址,所以不存在内存泄漏的情况,也是jvm内存区域中唯一一个没有OOME(out of memory error)定义的区域。

  虚拟机栈(JVM stack):当线程的每个方法在执行的时候都会创建一个栈帧(Stack Frame)用来存储方法中的局部变量、方法出口等,同时会将这个栈帧放入JVM栈中,方法调用完成时,这个栈帧出栈。每个线程都要一个自己的虚拟机栈来保存自己的方法调用时候的数据,因此虚拟机栈也是线程私有的。

  虚拟机栈中定义了两种异常,如果线程调用的栈深度大于虚拟机允许的最大深度,抛出StackOverFlowError,不过虚拟机基本上都允许动态扩展虚拟机栈的大小。这样的话线程可以一直申请栈,直到内存不足的时候,会抛出OOME(out of memory error)内存溢出。

  本地方法栈(Native Method Stack):本地方法栈与虚拟机栈类似,只是本地方法栈存放的栈帧是在native方法调用的时候产生的。有的虚拟机中会将本地方法栈和虚拟栈放在一起,因此本地方法栈也是线程私有的。

  堆(Heap):堆是java GC机制中最重要的区域。堆是为了放置“对象的实例”,对象都是在堆区上分配内存的,堆在逻辑上连续,在物理上不一定连续。所有的线程共用一个堆,堆的大小是可扩展的,如果在执行GC之后,仍没有足够的内存可以分配且堆大小不可再扩展,将会抛出OOME。

  方法区(Method Area):又叫静态区,用于存储类的信息、常量池等,逻辑上是堆的一部分,是各个线程共享的区域,为了与堆区分,又叫非堆。在永久代还存在时,方法区被用作永久代。方法区可以选择是否开启垃圾回收。jvm内存不足时会抛出OOME。

  直接内存(Direct Memory):直接内存指的是非jvm管理的内存,是机器剩余的内存。用基于通道(Channel)和缓冲区(Buffer)的方式来进行内存分配,用存储在JVM中的DirectByteBuffer来引用,当机器本身内存不足时,也会抛出OOME。

  举例说明:Object obj = new Object();

  obj表示一个本地引用,存储在jvm栈的本地变量表中,new Object()作为一个对象放在堆中,Object类的类型信息(接口,方法,对象类型等)放在堆中,而这些类型信息的地址放在方法区中。

  这里需要知道如何通过引用访问到具体对象,也就是通过obj引用如何找到new出来的这个Object()对象,主要有两种方法,通过句柄和通过直接指针访问。

  通过句柄:

  在java堆中会专门有一块区域被划分为句柄池,一个引用的背后是一个对象实例数据(java堆中)的指针和对象类型信息(方法区中)的指针,而这两个指针都是在java堆上的。这种方法是优势是较为稳定,但是速度不是很快。

  通过直接指针:

  一个引用背后是一个对象的实例数据,这个实例数据里面包含了“到对象类型信息的指针”。这种方式的优势是速度快,在HotSpot中用的就是这种方式。

  2.内存是如何分配和回收的

  内存分配主要是在堆上的分配,如前面new出来的对象,放在堆上,但是现代技术也支持在栈上分配,较为少见,本文不考虑。分配内存与回收内存的标准是八个字:分代分配,分代回收。那么这个代是什么呢?

  jvm中将对象根据存活的时间划分为三代:年轻代(Young Generation)、年老代(Old Generation)和永久代(Permannent Generation)。在jdk1.8中已经不再使用永久代,因此这里不再介绍。

  

  年轻代:又叫新生代,所有新生成的对象都是先放在年轻代。年轻代分三个区,一个Eden区,两个Survivor区,一个叫From,一个叫To(这个名字是动态变化的)。当Eden中满时,执行Minor GC将消亡的对象清理掉,仍存活的对象将被复制到Survivor中的From区,清空Eden。当这个From区满的时候,仍存活的对象将被复制到To区,清空From区,并且原From区变为To区,原To区变为From区,这样的目的是保证To区一直为空。当From区满无对象可清理或者From-To区交换的次数超过设定(HotSpot默认为15,通过-XX:MaxTenuringThreashold控制)的时候,仍存活的对象进入老年代。年轻代中Eden和Servivor的比例通过-XX:SerivorRation参数来配置,默认为8,也就时说Eden:From:To=8:1:1。年轻代的回收方式叫做Minor GC,又叫停止-复制清理法。这种方法在回收的时候,需要暂停其他所有线程的执行,导致效率很低,现在虽然有优化,但是仅仅是将停止的时间变短,并没有彻底取消这个停止。

  年老代:年老代的空间较大,当年老代内存不足时,将执行Major GC也叫Full GC。如果对象比较大,可能会直接分配到老年代上而不经过年轻代。用-XX:pertenureSizeThreashold来设定这个值,大于这个的对象会直接分配到老年代上。

  3.垃圾收集器

  在GC机制中,起作用的是垃圾收集器。HotSpot1.6中使用的垃圾收集器如下(有连线表示有联系):

  

  Serial收集器:新生代(年轻代)收集器,使用停止-复制算法,使用一个线程进行GC,其他工作线程暂停。

  ParNew收起:新生代收集器,使用停止-复制算法,Serial收集器的多线程版,用多个线程进行GC,其他工作线程暂停,关注缩短垃圾收集时间。

  Parallel Scavenge收集器:新生代收集器,使用停止-复制算法,关注CPU吞吐量,即运行用户代码的时间/总时间。

  Serial Old收集器:年老代收集器,单线程收集器,使用标记-整理算法(整理的方法包括sweep清理和compact压缩,标记-清理是先标记需要回收的对象,在标记完成后统一清楚标记的对象,这样清理之后空闲的内存是不连续的;标记-压缩是先标记需要回收的对象,把存活的对象都向一端移动,然后直接清理掉端边界以外的内存,这样清理之后空闲的内存是连续的)。

  Parallel Old收集器:老年代收集器,多线程收集器,使用标记-整理算法(整理的方法包括summary汇总和compact压缩,标记-压缩与Serial Old一样,标记-汇总是将幸存的对象复制到预先准备好的区域,再清理之前的对象)。

  CMS(Concurrent Mark Sweep)收集器:老年老代收集器,多线程收集器,关注最短回收时间停顿,使用标记-清除算法,用户线程可以和GC线程同时工作。

  G1收集器:JDK1.7中发布,使用较少,不作介绍。

  Java GC是一个非常复杂的机制,想要详细说清楚他需要很多时间,如有错误恳请指正。

  

java虚拟机的内存分配与回收机制的更多相关文章

  1. Java基础-Java中的内存分配与回收机制

    Java基础-Java中的内存分配与回收机制 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一. 二.

  2. 【java虚拟机】内存分配与回收策略

    作者:平凡希 原文地址:https://www.cnblogs.com/xiaoxi/p/6557473.html 前言 对象的内存分配,往大的方向上讲,就是在堆上分配,少数情况下也可能会直接分配在老 ...

  3. Java虚拟机学习笔记——JVM垃圾回收机制

    Java虚拟机学习笔记——JVM垃圾回收机制 Java垃圾回收基于虚拟机的自动内存管理机制,我们不需要为每一个对象进行释放内存,不容易发生内存泄漏和内存溢出问题. 但是自动内存管理机制不是万能药,我们 ...

  4. Java深入 - Java 内存分配和回收机制

    Java的GC机制是自动进行的,和c语言有些区别需要程序员自己保证内存的使用和回收. Java的内存分配和回收也主要在Java的堆上进行的,Java的堆中存储了大量的对象实例,所以Java的堆也叫GC ...

  5. Java深入 - Java 内存分配和回收机制-转

    Java的GC机制是自动进行的,和c语言有些区别需要程序员自己保证内存的使用和回收. Java的内存分配和回收也主要在Java的堆上进行的,Java的堆中存储了大量的对象实例,所以Java的堆也叫GC ...

  6. 《深入理解Java虚拟机》内存分配策略

    上节学习回顾 1.判断对象存活算法:引用计数法和可行性分析算法 2.垃圾收集算法:标记-清除算法.复制算法.标记-整理算法 3.垃圾收集器: Serial:新生代收集器,采用复制算法,单线程. Par ...

  7. JAVA虚拟机内存分配与回收机制

    Java虚拟机(Java Virtual Machine) 简称JVM Java虚拟机是一个想象中的机器,在实际的计算机上通过软件模拟来实现.Java虚拟机有自己想象中的硬件,如处理器.堆栈.寄存器等 ...

  8. Java虚拟机:内存分配策略

    版权声明:本文为博主原创文章,转载请注明出处,欢迎交流学习! Java中提倡的自动内存管理机制最终可以归结为自动化的解决两个问题:给对象分配内存和回收分配给对象的内存.在之前的博客中已经详细讲解了内存 ...

  9. java虚拟机之内存分配

    Java 的自动内存管理主要是针对对象内存的回收和对象内存的分配.同时,Java 自动内存管理最核心的功能是 堆 内存中对象的分配与回收. JDK1.8之前的堆内存示意图: 从上图可以看出堆内存分为新 ...

随机推荐

  1. freemarker.core.InvalidReferenceException

    1.错误描述 freemarker.core.InvalidReferenceException:on line 68,column 18 in ftl/inc/incPro.ftl p.mainSe ...

  2. oracle 常用知识积累

    一.  基本操作 1.  表操作 1.1 复制建表 create table test as select * from dept; --从已知表复制数据和结构 create table test a ...

  3. Ball HDU - 4811

    Jenny likes balls. He has some balls and he wants to arrange them in a row on the table. Each of tho ...

  4. MessageFormat.format用法

    用法一: package gacl.request.study; import java.io.IOException; import java.text.MessageFormat; import ...

  5. CSS3 Tranform 3D 的应用

    CSS3 Tranform 3D 的应用 一.perspective 属性 1. 作用: 设置元素被查看位置的视图,类似于眼睛到屏幕的距离,一般跟 perspective-origin 共同作用在一个 ...

  6. js中百分比运算,大型数据会算错

    改法:被除数乘100在做除法运算,就能改掉算错

  7. link-cut-tree 简单介绍

    link-cut-tree 简单介绍 前言:这个算法似乎机房全都会,就我不会了TAT...强行搞了很久,勉强照着别人代码抄了一遍qwq 这个本人看论文实在看不懂,太菜了啊!!! 只好直接看如何实现.. ...

  8. 【CF235C】Cyclical Quest(后缀自动机)

    [CF235C]Cyclical Quest(后缀自动机) 题面 洛谷 题解 大致翻译: 给定一个串 然后若干组询问 每次也给定一个串 这个串可以旋转(就是把最后一位丢到最前面这样子) 问这个串以及其 ...

  9. 【BZOJ2049】洞穴勘测(Link-Cut Tree)

    [BZOJ2049]洞穴勘测(Link-Cut Tree) 题面 题目描述 辉辉热衷于洞穴勘测. 某天,他按照地图来到了一片被标记为JSZX的洞穴群地区.经过初步勘测,辉辉发现这片区域由n个洞穴(分别 ...

  10. 【HNOI 2002 】营业额统计(splay)

    题面 Description Tiger最近被公司升任为营业部经理,他上任后接受公司交给的第一项任务便是统计并分析公司成立以来的营业情况. Tiger拿出了公司的账本,账本上记录了公司成立以来每天的营 ...