浅析Java中的GC垃圾回收器的意义及与GC的交互

对象是使用new创建的,但是并没有与之相对应的delete操作来回收对象占用的内存。当我们完成对某个对象的使用时,只需停止对该对象的引用:将我们的引用改变为指向其他对象或指向null;或者从方法中返回,使得该方法的局部变量不复存在,从而使得对这些局部变量的引用变为不指向任何对象。不再被引用的对象被称为垃圾(garbage),查找并回收这些对象的过程叫做垃圾回收(garbage collection) o

  Java虚拟机利用垃圾回收来保证被引用的对象将会在内存中保留,同时会释放在执行代码中通过任何引用都不可达的对象所占用的存储空间。这是一种强保证—如果顺着从根引用(即在执行代码中可以直接访问的引用)开始的引用链可以到达某个对象,那么该对象就不会被回收。

  简言之,当我们从任何可执行代码都无法到达某个对象时,它所占用的空间就可以被回收。注意,我们用的是“可以”这个词,因为内存空间是否回收是由垃圾回收器来决定的,通常情况下,只有需要更多的内存空间或者为了避免发生内存溢出时,垃圾回收器才会运行。但是程序可能在没有发生内存溢出,甚至在没有接近内存溢出的时候就退出了,所以可能根本就不需要执行垃圾回收。在当前执行的所有方法中,如果所有变量都不包含指向某个对象的引用,并且从这些变量出发,顺着引用链在所有域或数组元素中也找不到对这个对象的引用,那么我们就说这个对象是“不可达的”。

  垃圾回收意味着我们永远不必担心出现虚悬引用(dangling reference)。在那些可以由程序员直接控制何时删除对象的系统中,程序员可以删除某个其他对象还在引用的对象,如果程序员删除了这样的对象,那么还在引用被删除对象的引用就会变为虚悬的,因为它们引用的是操

  作系统认为是可分配的内存空间(但实际上该空间已经被释放)。系统可以将这个可分配空间分配给新的对象,这样那些原来指向该空间的引用实际上得到的对象与它们所预期的就完全不同了。在这种情况下,当程序使用存储于这个空间中的值并将其当作它们并不属于的对象来操作时,就可能会引起不可预知的灾难。垃圾回收为我们解决了虚悬引用问题,因为所有仍然被引用的对象都不会被当作垃圾回收,所以它们所占用的空间也不可能被释放。垃圾回收同时还解决了意外地多次删除同一个对象的问题—这种问题也会引发灾难。 垃圾对象的回收并不需要我们的介入,但是回收垃圾会占用一定的系统资源。大量对象的创建和回收对时间关键的应用会产生干扰,因此我们在设计这种系统时,要审慎地处理创建的对象数量,以便减少要回收的垃圾数量。

  垃圾回收并不能保证内存总是会有空间来创建新对象。例如,如果我们不停地创建对象,并把这些对象置于某个列表中,那么当没有足够的空间来创建新对象,同时也没有任何未被引用的对象时,就无法再创建新对象了。如果我们让上述列表保持对不再需要的对象的引用,那么就会造成内存泄漏。垃圾回收解决了很多(但并非全部)的内存分配问题。

与垃圾回收器交互
尽管Java语言本身没有任何显式地处置空闲对象的方法,我们还是可以通过直接调用垃圾回收器来寻找不再使用的对象。Runtime类以及system类中的一些便捷方法使得我们可以调用垃圾回收器,请求运行所有待运行的终结器,或者查看当前的内存状态:

  .public void gc Q:该方法请求Java虚拟机花费精力去回收不再使用的对象,以便能够重用这些对象所占据的内存。

  .public void runFinalization():该方法请求Java虚拟机花费精力去运行如下的终结器:那些已经被发现是不可达的,但是其终结器还未执行的对象。

  “public long freememory():返回系统内存可用字节的估测数。

  ·public long total Memory ():返回系统内存的总字节数。

  .public long maxmemoryo:返回Java虚拟机可用的系统内存的最大字节数。如果操作系统对Java虚拟机没有内存使用上的限制,将返回Long . MAX-VALUE. Java中没有任何用来设置系统最大内存的方法,通常,Java虚拟机是通过命令行或者其他配置选项来设置这个值的。

  要调用上述方法,我们需要通过静态方法Runtime.getRuntime来获取对当前Runtime对象的引用。而system类支持静态的gc和runFinalization方法,它们将调用当前Runt-ime对象上的相应方法;换句话说,System.gc()与Runtime.getRuntime().gc()方法是等价的。

  在调用Runtime.gc()方法时,垃圾回收器可能并不能释放出任何额外的内存,因为可能并没有垃圾可以回收,而且并非所有的垃圾回收器都可以按需发现可回收对象。因此调用垃圾回收器可能不会产生任何效果。然而,在创建大量的对象之前,特别是在垃圾回收的开销可能会对其造成影响的时间关键的应用中,调用Runtime.gc()方法还是可取的。执行它有两点潜在的好处:第一点是我们在运行应用程序之前可以得到尽可能多的内存,第二点是我们可以降低执行任务期间垃圾回收器运行的可能性。下面的方法在运行时刻积极地释放了可以释放的所有空间:

  public static vo记ful1GC(){

  Runtime rt=Runtime.getRuntime();

  long isFree=rt.freeMemory ();

  long wasFree;

  do{

  wasFree=isFree;

  rt.runFinalization ();

  rt.gc();

  isFree二rt.freeMemory();

  }while (isFree>wasFree);

  }

  该方法在不断地循环,通过连续调用runFinalization和gc方法,freememory的值不断地增大。当空闲内存的数量不再增大时,该方法的循环也就结束了。

  我们通常不需要调用runFinalization方法,因为finalize方法是由垃圾回收器异步调用的。在某些情况下,例如某项可以由finalize方法回收的资源被耗尽时,通过调用run-Finalization来强制执行尽可能多的终结才会显得有用。但是请记住,我们并不能保证任何等待被终结的对象都在使用这项资源,因此runFinalization可能不会有任何作用。

  fullGc方法对于大多数应用程序来说都显得过于激进。在需要强制进行垃圾回收的特殊情况下,对system.gc方法的单次调用所收集到的垃圾即便不是全部的可利用垃圾,也是其中的绝大部分,因此重复调用会降低垃圾回收的产出率,而且在许多系统中,这些重复调用是毫无产出的。

时间: 2015-12-24

深入了解Java GC的工作原理

JVM学习笔记之JVM内存管理和JVM垃圾回收的概念,JVM内存结构由堆.栈.本地方法栈.方法区等部分组成,另外JVM分别对新生代下载地址  和旧生代采用不同的垃圾回收机制. 首先来看一下JVM内存结构,它是由堆.栈.本地方法栈.方法区等部分组成,结构图如下所示. JVM学习笔记 JVM内存管理和JVM垃圾回收 JVM内存组成结构 JVM内存结构由堆.栈.本地方法栈.方法区等部分组成,结构图如下所示: 1)堆 所有通过new创建的对象的内存都在堆中分配,其大小可以通过-Xmx和-Xms来控制.堆

浅谈关于Java的GC垃圾回收器的一些基本概念

一.基本回收算法 1. 引用计数(Reference Counting) 比较古老的回收算法.原理是此对象有一个引用,即增加一个计数,删除一个引用则减少一个计数.垃圾回收时,只用收集计数为0的对象.此算法最致命的是无法处理循环引用的问题. 2. 标记-清除(Mark-Sweep) 此算法执行分两阶段.第一阶段从引用根节点开始标记所有被引用的对象,第二阶段遍历整个堆,把未标记的对象清除.此算法需要暂停整个应用,同时,会产生内存碎片. 3. 复制(Copying) 此算法把内存空间划为两个相等的区域

全面解析Java中的GC与幽灵引用

Java 中一共有 4 种类型的引用 : StrongReference. SoftReference. WeakReference 以及 PhantomReference (传说中的幽灵引用 呵呵), 这 4 种类型的引用与 GC 有着密切的关系,  让我们逐一来看它们的定义和使用场景 : 1. Strong ReferenceStrongReference 是 Java 的默认引用实现,  它会尽可能长时间的存活于 JVM 内, 当没有任何对象指向它时 GC 执行后将会被回收 Java代码

Java中垃圾回收器GC对吞吐量的影响测试

在看内存管理术语表的时候偶然发现了"Pig in the Python(注:有点像中文里的贪心不足蛇吞象)"的定义,于是便有了这篇文章.表面上看,这个术语说的是GC不停地将大对象从一个分代提升到另一个分代的情景.这么做就好比巨蟒整个吞食掉它的猎物,以至于它在消化的时候都没办法移动了. 在接下来的这24个小时里我的头脑中充斥着这个令人窒息的巨蟒的画面,挥之不去.正如精神病医生所说的,消除恐惧最好的方法就是说出来.于是便有了这篇文章.不过接下的故事我们要讲的不是蟒蛇,而是GC的调优.我对天

Java中GC的工作原理详细介绍

Java中GC的工作原理 引子:面试时被问到垃圾回收机制,只是粗略的讲'程序员不能直接对内存操作,jvm负责对已经超过作用域的对象回收处理',面官表情呆滞,也就没再继续深入. 转文: 一个优秀的Java程序员必须了解GC的工作原理.如何优化GC的性能.如何与GC进行有限的交互,有一些应用程序对性能要求较高,例如嵌入式系统.实时系统等,只有全面提升内存的管理效率,才能提高整个应用程序的性能.本文将从GC的工作原理.GC的几个关键问题进行探讨,最后提出一些Java程序设计建议,如何从GC角度提高Ja

从JVM的内存管理角度分析Java的GC垃圾回收机制

一个优秀的Java程序员必须了解GC的工作原理.如何优化GC的性能.如何与GC进行有限的交互,因为有一些应用程序对性能要求较高,例如嵌入式系统.实时系统等,只有全面提升内存的管理效率 ,才能提高整个应用程序的性能.本篇文章首先简单介绍GC的工作原理之后,然后再对GC的几个关键问题进行深入探讨,最后提出一些Java程序设计建议,从GC角度提高Java程序的性能.     GC的基本原理     Java的内存管理实际上就是对象的管理,其中包括对象的分配和释放.     对于程序员来说,分配对象使用

Java GC 机制与内存分配策略详解

Java GC 机制与内存分配策略详解 收集算法是内存回收的方法论,垃圾收集器是内存回收的具体实现 自动内存管理解决的是:给对象分配内存 以及 回收分配给对象的内存 为什么我们要了解学习 GC 与内存分配呢? 在 JVM 自动内存管理机制的帮助下,不再需要为每一个new操作写配对的delete/free代码.但出现内存泄漏和溢出的问题时,如果不了解虚拟机是怎样使用内存的,那么排查错误将是一项非常艰难的工作. GC(垃圾收集器)在对堆进行回收前,会先确定哪些对象"存活",哪些已经&quo

JAVA垃圾收集器与内存分配策略详解

引言 垃圾收集技术并不是Java语言首创的,1960年诞生于MIT的Lisp是第一门真正使用内存动态分配和垃圾收集技术的语言.垃圾收集技术需要考虑的三个问题是: 1.哪些内存需要回收 2.什么时候回收 3.如何回收 java内存运行时区域的分布,其中程序计数器,虚拟机栈,本地方法区都是随着线程而生,随线程而灭,所以这几个区域就不需要过多考虑回收问题.但是堆和方法区就不一样了,只有在程序运行期间我们才知道会创建哪些对象,这部分内存的分配和回收都是动态的.垃圾收集器所关注的就是这部分内存. 一 对象

Java语言中的内存泄露代码详解

Java的一个重要特性就是通过垃圾收集器(GC)自动管理内存的回收,而不需要程序员自己来释放内存.理论上Java中所有不会再被利用的对象所占用的内存,都可以被GC回收,但是Java也存在内存泄露,但它的表现与C++不同. JAVA中的内存管理 要了解Java中的内存泄露,首先就得知道Java中的内存是如何管理的. 在Java程序中,我们通常使用new为对象分配内存,而这些内存空间都在堆(Heap)上. 下面看一个示例: public class Simple { public static vo

C语言动态内存分配的详解

C语言动态内存分配的详解 1.为什么使用动态内存分配 数组在使用的时候可能造成内存浪费,使用动态内存分配可以解决这个问题. 2. malloc和free C函数库提供了两个函数,malloc和free,分别用于执行动态内存分配和释放. (1)void *malloc(size_t size); malloc的参数就是需要分配的内存字节数.malloc分配一块连续的内存.如果操作系统无法向malloc提供更多的内存,malloc就返回一个NULL指针. (2)void free(void *poi

C++ 中继承与动态内存分配的详解

C++ 中继承与动态内存分配的详解 继承是怎样与动态内存分配进行互动的呢?例如,如果基类使用动态内存分配,并重新定义赋值和复制构造函数,这将怎样影响派生类的实现呢?这个问题的答案取决于派生类的属性.如果派生类也使用动态内存分配,那么就需要学习几个新的小技巧.下面来看看这两种情况: 一.派生类不使用new 派生类是否需要为显示定义析构函数,复制构造函数和赋值操作符呢? 不需要! 首先,来看是否需要析构函数,如果没有定义析构函数,编译器将定义一个不执行任何操作的默认构造函数.实际上,派生类的默认构造

C语言 动态内存分配的详解及实例

1. 动态内存分配的意义 (1)C 语言中的一切操作都是基于内存的. (2)变量和数组都是内存的别名. ①内存分配由编译器在编译期间决定 ②定义数组的时候必须指定数组长度 ③数组长度是在编译期就必须确定的 (3)但是程序运行的过程中,可能需要使用一些额外的内存空间 2. malloc 和 free 函数 (1)malloc 和 free 用于执行动态内存分配的释放 (2)malloc 所分配的是一块连续的内存 (3)malloc 以字节为单位,并且返回值不带任何的类型信息:void* mallo

Java类加载机制实现流程及原理详解

前言 我们知道,Java项目编译后会生成许许多多的class文件,class文件保存着类的描述信息.虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验.转化解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制. 类的生命周期 类从被加载到虚拟机内存中开始,到卸载出内存位置,他的整个生命周期包括: 加载验证准备解析初始化使用卸载 这七个阶段.画个图就是下面这样: 其中,类加载的过程包括了加载.验证.准备.解析.初始化这五个阶段.其中加载.验证.准备.初始

浅谈Java内存区域划分和内存分配策略

如果不知道,类的静态变量存储在那? 方法的局部变量存储在那? 赶快收藏 Java内存区域主要可以分为共享内存,堆.方法区和线程私有内存,虚拟机栈.本地方法栈和程序计数器.如下图所示,本文将详细讲述各个区域,同时也会讲述创建对象过程,内存分配策略, 和对象访问定位原理.觉得写得好的,可以点个收藏,绝对不亏. Java内存区域 程序计数器 程序计数器,可以看作程序当前线程所执行的字节码行号指示器.字节码解释器工作时就是通过改变计数器的值来选取下一条需要执行的字节码指令,分支.循环.跳转.异常处理都需

Java内存模型JMM详解

Java Memory Model简称JMM, 是一系列的Java虚拟机平台对开发者提供的多线程环境下的内存可见性.是否可以重排序等问题的无关具体平台的统一的保证.(可能在术语上与Java运行时内存分布有歧义,后者指堆.方法区.线程栈等内存区域). 并发编程有多种风格,除了CSP(通信顺序进程).Actor等模型外,大家最熟悉的应该是基于线程和锁的共享内存模型了.在多线程编程中,需要注意三类并发问题: ·原子性 ·可见性 ·重排序 原子性涉及到,一个线程执行一个复合操作的时候,其他线程是否能够看

Java内存模型知识详解

1. 概述 多任务和高并发是衡量一台计算机处理器的能力重要指标之一.一般衡量一个服务器性能的高低好坏,使用每秒事务处理数(Transactions Per Second,TPS)这个指标比较能说明问题,它代表着一秒内服务器平均能响应的请求数,而TPS值与程序的并发能力有着非常密切的关系.在讨论Java内存模型和线程之前,先简单介绍一下硬件的效率与一致性. 2.硬件的效率与一致性 由于计算机的存储设备与处理器的运算能力之间有几个数量级的差距,所以现代计算机系统都不得不加入一层读写速度尽可能接近处理