Java虚拟机内存管理机制

自动内存管理机制

Java虚拟机(JVM)在执行Java程序过程中会把它所管理的内存划分为若干个不同的数据区域。这些区域都有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而存在,有的区域则是依赖用户线程的启动和结束而建立和销毁。根据《Java虚拟机规范 第2版》规定,运行时数据区包括

1、程序计数器

一块较小的内存空间,不在Ram上,而是直接划分在CPU上的,程序员无法直接操作它。当前线程所执行的字节码的行号指示器,通过改变这个计数器的值来选取下一条需要执行的字节码指令。每条线程都有一个独立的程序计数器,属于线程私有的内存。该区域是唯一一个没有规定任何OutOfMemoryError(内存溢出)情况的区域。

2、Java虚拟机栈

描述Java方法执行的内存模型,每个方法被执行的时候都会同时创建一个栈帧用于存储局部变量表、操作栈、动态链接、方法出口等信息。每个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。生命周期与线程相同,也属于线程私有的内存。

根据规范该区域存在两种异常状况:如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常,如果虚拟机栈进行动态扩展时无法申请到足够的内存时会抛出OutOfMemoryError(内存溢出)异常。

3、本地方法栈

为虚拟机使用的native方法服务。Java类的祖先类Object中有众多Native方法,如hashCode()、wait()等,他们的执行很多时候是借助于操作系统,但是JVM需要对他们做一些规范,来处理他们的执行过程。有的虚拟机(如:Sun HotSpot虚拟机)直接就把本地方法栈和虚拟机栈合二为一。也会抛出StackOverflowError和OutOfMemoryError异常。属于线程私有的内存。

4、堆

被所有线程共享的一块内存区域,在虚拟机启动时创建。几乎所有的对象实例以及数组都要在堆上分配内存。Java堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可。 Java堆是垃圾收集器管理的主要区域,也被称为“GC堆”,在32位系统上最大为2G,64位系统上无限制。可通过-Xms和-Xmx控制。Java性能的优化,主要就是针对这部分内存的。

如果堆中没有内存完成实例分配,并且堆也无法再进行扩展时,将会抛出OutOfMemoryError异常。

由于现在垃圾收集器基本采用分代收集算法,所以Java堆细分为:年轻代(Eden区、From Survivor区、To Survivor区) 和 年代。Java堆上还可能划分出线程私有的分配缓冲区(Thread Local Allocation Buffer,TLAB)

  • 年轻代(New):年轻代用来存放JVM刚分配的Java对象
  • 年老代(Tenured):年轻代中经过垃圾回收没有回收掉的对象将被Copy到年老代
  • Eden:Eden用来存放JVM刚分配的对象
  • Survivor:两个Survivor空间一样大,当Eden中的对象经过垃圾回收没有被回收掉时(对象仍然存活),会在两个Survivor之间来回Copy,当满足某个条件,比如Copy次数,就会被Copy到年老代。显然,Survivor只是增加了对象在年轻代中的逗留时间,增加了被垃圾回收的可能性。

5、方法区(非堆、”永久代“)

线程共享的内存区域,存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。垃圾收集器在这个区域是比较少出现的,这个区域的内存回收目标主要是针对常量池的回收和对类型的卸载。可通过-XX:PermSize -XX:MaxPermSize 等参数调整其大小。

当方法区无法满足内存分配需求时,将抛出OutOfMemoryError异常。

运行时常量池

方法区的一部分,class文件的常量池用于存放编译期生成的各种字面值和符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。当常量池无法满足内存分配需求时,将抛出OutOfMemoryError异常。

6、直接内存

在JDK1.4中新加入类NIO类,引入了一种基于通道与缓冲区的I/O方式,它可以使用Native函数库直接分配堆外内存,即我们所说的直接内存,这样在某些场景中会提高程序的性能。直接内存不是虚拟机运行时数据区的一部分(当然也不会受到Java堆大小的限制),也不是规范规定的,但这部分内存会被频繁使用,也可能抛出OutOfMemoryError异常。



垃圾收集(GC)

Java语言对程序员做了一个美好的承诺:程序员无需去管理内存,因为JVM有垃圾回收(GC),会去自动进行垃圾回收。其实不然:

  • 垃圾回收并不会按照程序员的要求,随时进行GC。
  • 垃圾回收并不会及时的清理内存,尽管有时程序需要额外的内存。
  • 程序员不能对垃圾回收进行控制。

因为上面这些事实,以致我们在写程序的时候,只能根据垃圾回收的规律,合理安排内存,这就要求我们必须彻底了解JVM的内存管理机制,这样才能随心所欲,将程序控制于鼓掌之中。

GC需要完成的三件事:

  • 哪些内存需要回收?
  • 什么时候回收?
  • 如何回收?(垃圾收集算法)

1、哪些内存需要回收?

在上面介绍的五大区中,有三个是不需要进行垃圾回收的:程序计数器、JVM栈、本地方法栈。因为它们的生命周期是和线程同步的,随着线程的销毁,它们占用的内存会自动释放,所以只有方法区和堆需要进行GC。

GC应该回收这样一些对象,这些对象没有任何引用指向(即对象已死)。 Java使用根搜索算法判断对象是否存活。基本思路是:通过一系列的名为“GC Roots”的对象作为起始点,当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。

Java语言里,可作为GC Roots的对象包括下面几种:

  • 虚拟机栈(栈帧中的本地变量表)中的引用的对象。
  • 方法区中的类静态属性引用的对象。
  • 方法区中的常量引用的对象。
  • 本地方法栈中JNI(Native方法)的引用的对象。

四种引用的GC特点

JDK1.2之后,对引用进行了扩充,引入了强、软、若、虚四种引用,被标记为这四种引用的对象,在GC时分别有不同的意义:

  • 强引用(Strong Reference)---就是为刚被new出来的对象所加的引用,它的特点就是,永远不会被回收
  • 软引用(Soft Reference)---声明为软引用的类,是可被回收的对象,如果JVM内存并不紧张,这类对象可以不被回收,如果内存紧张,则会被回收。此处有一个问题,既然被引用为软引用的对象可以回收,为什么不去回收呢?其实我们知道,Java中是存在缓存机制的,就拿字面量缓存来说,有些时候,缓存的对象就是当前可有可无的,只是留在内存中如果还有需要,则不需要重新分配内存即可使用,因此,这些对象即可被引用为软引用,方便使用,提高程序性能。
  • 弱引用(Weak Reference)---弱引用的对象就是一定需要进行垃圾回收的,不管内存是否紧张,当进行GC时,标记为弱引用的对象一定会被清理回收。
  • 虚引用(Phantom Reference)---虚引用弱的可以忽略不计,JVM完全不会在乎虚引用,其唯一作用就是做一些跟踪记录,辅助finalize函数的使用。

2、什么时候回收?

  • 当年轻代内存满时,会引发一次普通GC(Minor GC),该GC仅回收年轻代。需要强调的时,年轻代满是指Eden代满,Survivor满不会引发GC
  • 当年老代满时会引发Full GC,Full GC将会同时回收年轻代、年老代
  • 当永久代满时也会引发Full GC,会导致Class、Method元信息的卸载

两种GC的区别:

何时会抛出OutOfMemoryException?

并不是内存被耗空的时候才抛出,满足如下两个条件将触发OutOfMemoryException:

  • JVM98%的时间都花费在内存回收
  • 每次回收的内存小于2%

3、如何回收?(垃圾收集算法)

常见的GC算法:

1)标记-清除算法(Mark-Sweep)

最基础的GC算法,将需要进行回收的对象做标记,之后扫描,有标记的进行回收,这样就产生两个步骤:标记和清除。这个算法效率不高,而且在清理完成后会产生内存碎片,这样,如果有大对象需要连续的内存空间时,还需要进行碎片整理,所以,此算法需要改进。

2)复制算法(Copying)

前面我们谈过,新生代内存分为了三份,Eden区和2块Survivor区,一般Sun的JVM会将Eden区和Survivor区的比例调为8:1,保证有一块Survivor区是空闲的,这样,在垃圾回收的时候,将不需要进行回收的对象放在空闲的Survivor区,然后将Eden区和第一块Survivor区进行完全清理,这样有一个问题,就是如果第二块Survivor区的空间不够大怎么办?这个时候,就需要当Survivor区不够用的时候,暂时借持久代的内存用一下。此算法适用于新生代

3)标记-整理(或叫压缩)算法(Mark-Compact)

和标记-清楚算法前半段一样,只是在标记了不需要进行回收的对象后,将标记过的对象移动到一起,使得内存连续,这样,只要将标记边界以外的内存清理就行了。此算法适用于持久代

4)分代收集算法

根据各个年代的特点采用最适当的收集算法。

常见的垃圾收集器:



Java堆内存分配策略

对象优先在Eden区分配

大对象直接进入年老代

长期存活的对象将进入年老代

动态对象年龄判断

空间分配担保

---Java和C++之间有一堵有内存分配和垃圾回收技术围成的墙,墙外的人想进去,墙里的人想出去!

C、C++程序员有时苦于内存泄露,内存管理是件令人头痛的事儿,但是Java程序员呢,又羡慕C++程序员,自己可以控制一切,这样就不会在内存管理方面显得束手无策。

Java虚拟机内存管理机制,布布扣,bubuko.com

时间: 2024-10-13 23:53:50

Java虚拟机内存管理机制的相关文章

深入理解Java虚拟机—内存管理机制

前面说过了类的加载机制,里面讲到了类的初始化中时用到了一部分内存管理的知识,这里让我们来看下Java虚拟机是如何管理内存的. 先让我们来看张图 有些文章中对线程隔离区还称之为线程独占区,其实是一个意思了.下面让我们来详细介绍下这五部分: 运行时数据区 Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域,这些区域都拥有自己的用途,并随着JVM进程的启动或者用户线程的启动和结束建立和销毁. 先让我们了解下进程和线程的区别: 进程是资源分配的最小单位,线程是程序执行的

关于java虚拟机内存管理的一些讲解

java数据类型: 1)原始类型:Primitive Types(原始值) 数值类型(Numeric Types) 整型类型(Integral Types),浮点类型(Floating-Point Types) 布尔类型(Boolean Types) returnAddress类型:表示一条字节码指令的操作码(Opcode).在所有的虚拟机支持的原始类型之中,只有 returnAddress 类型是不能直接 Java 语言的数据类型对应起来的. 2)引用类型:Reference Types(引用

Java自动内存管理机制学习(一):Java内存区域与内存溢出异常

备注:本文引用自<深入理解Java虚拟机第二版> 2.1 运行时数据区域 Java虚拟机在执行Java程序的过程中把它所管理的内存划分为若干个不同的数据区域.这些区域都有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而存在,有些区域则依赖用户线程的启动和结束而建立和销毁.如下图所示: 2.1.1 程序计数器 程序计数器是一块较小的内存空间,它是线程的私有内存,可以看作时当前线程所执行的字节码的行号指示器.在虚拟机的概念模型里(仅是概念模型,各种虚拟机可能会通过一些更高效的方式去

十分良心!全网最详细的Java 自动内存管理机制及性能优化教程

同样的,先来个思维导图预览一下本文结构. 一图带你看完本文 一.运行时数据区域 首先来看看Java虚拟机所管理的内存包括哪些区域,就像我们要了解一个房子,我们得先知道这个房子大体构造.根据<Java虚拟机规范(Java SE 7 版)>的规定,请看下图: Java 虚拟机运行时数据区 1.1 程序计数器 程序计数器是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器. 由于 Java 虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个

(三)java虚拟机内存管理和线程独占区和线程共享区

一.内存管理 二.线程独占区之程序计数器(Program Counter Register) 程序计数器是一块较小的内存空间,它可以看做是当前线程所执行的字节码的行号指示器.在虚拟机的概念模型里,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支.循环.跳转.异常处理.线程恢复等基础功能都需要依赖这个计数器来完成. 如果线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址:如果正在执行的是Native方法,这个计数器值则为空(Und

探秘Java虚拟机——内存管理与垃圾回收

详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt106 1.Java虚拟机运行时的数据区 2.常用的内存区域调节参数 -Xms:初始堆大小,默认为物理内存的1/64(<1GB):默认(MinHeapFreeRatio参数可以调整)空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制 -Xmx:最大堆大小,默认(MaxHeapFreeRatio参数可以调整)空余堆内存大于70%时,JVM会减少堆直到 -Xms的最小

java自动内存管理机制

java程序员把内存管理的工作交给虚拟机,一旦出现内存泄露或者溢出问题,如果不了解内存是怎样工作的,那么排查错误将是一件异常艰难的工作. java内存区域与内存溢出异常 java运行时数据区域划分: 线程隔离的 1.程序计数器(Program Counter Register) 当前线程执行代码的行号指示器,当线程切换并分配处理器执行时间,为了保证线程恢复到正确的执行位置,每个线程都有独立的计数器 2.虚拟机栈(VM Stack) 虚拟机栈描述的是java方法执行的内存模型:每个方式执行的同时会

探秘Java虚拟机——内存管理与垃圾回收(转)

本文主要是基于Sun JDK 1.6 Garbage Collector(作者:毕玄)的整理与总结,原文请读者在网上搜索. 1.Java虚拟机运行时的数据区 2.常用的内存区域调节参数 -Xms:初始堆大小,默认为物理内存的1/64(<1GB):默认(MinHeapFreeRatio参数可以调整)空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制 -Xmx:最大堆大小,默认(MaxHeapFreeRatio参数可以调整)空余堆内存大于70%时,JVM会减少堆直到 -Xms的最小限制

Java虚拟机内存管理原理基础入门

Jdk:Java程序设计语言.Java虚拟机.Java API类库. Jdk是用于支持Java程序开发的最小环境. Jre:Java API类库中的Java SE API子集.Java虚拟机. Jre是支持Java程序运行的标准环境. Program Counter Register:较小的内存空间,可以看作当前线程所执行的字节码的行号指示器.是唯一一个Java虚拟机规范中没有规定OutOfMemoryError的区域. VM Stack:生命周期和线程相同,它描述了Java方法执行的内存模型: