浅析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方法的单次调用所收集到的垃圾即便不是全部的可利用垃圾,也是其中的绝大部分,因此重复调用会降低垃圾回收的产出率,而且在许多系统中,这些重复调用是毫无产出的。

(0)

相关推荐

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

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

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

    一个优秀的Java程序员必须了解GC的工作原理.如何优化GC的性能.如何与GC进行有限的交互,因为有一些应用程序对性能要求较高,例如嵌入式系统.实时系统等,只有全面提升内存的管理效率 ,才能提高整个应用程序的性能.本篇文章首先简单介绍GC的工作原理之后,然后再对GC的几个关键问题进行深入探讨,最后提出一些Java程序设计建议,从GC角度提高Java程序的性能.     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

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

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

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

  • 浅析JAVA中的内存结构、重载、this与继承

    一.对象在JVM的内存结构 JAVA内存管理由JVM来管理. 1)堆,所有new出来的对象(包括成员变量) 2)栈,所有局部变量(包括方法的参数) 3)方法区,class字节码文件(包括方法,静态数据) 1.引用变量指向null时,会发生空指针异常 public class student { int age; String name; public student(int age,String name){ this.age=age; this.name=name; } public stat

  • 浅析Java内存模型与垃圾回收

    1.Java内存模型 Java虚拟机在执行程序时把它管理的内存分为若干数据区域,这些数据区域分布情况如下图所示: 程序计数器:一块较小内存区域,指向当前所执行的字节码.如果线程正在执行一个Java方法,这个计数器记录正在执行的虚拟机字节码指令的地址,如果执行的是Native方法,这个计算器值为空. Java虚拟机栈:线程私有的,其生命周期和线程一致,每个方法执行时都会创建一个栈帧用于存储局部变量表.操作数栈.动态链接.方法出口等信息. 本地方法栈:与虚拟机栈功能类似,只不过虚拟机栈为虚拟机执行J

  • 浅析java中stringBuilder的用法

    String对象是不可改变的.每次使用 System.String类中的方法之一时,都要在内存中创建一个新的字符串对象,这就需要为该新对象分配新的空间.在需要对字符串执行重复修改的情况下,与创建新的 String对象相关的系统开销可能会非常昂贵.如果要修改字符串而不创建新的对象,则可以使用System.Text.StringBuilder类.例如,当在一个循环中将许多字符串连接在一起时,使用 StringBuilder类可以提升性能. 通过用一个重载的构造函数方法初始化变量,可以创建 Strin

  • 浅析java中String类型中“==”与“equal”的区别

    一.前言 1.1.首先很多人都知道,String中用"=="比较的是地址,用equals比较的是内容,很多人对此用的是记忆法,通过记忆来加强此的引用,但是其真正的原理其实并不难,当我们真正明白其为什么的时候,用起来也会更加灵活,更加有底气(形容得不太好,朋友别见怪): 二相关知识的准备 类型常量池 运行时常量池 字符串常量池 我们今天讨论的主题是当然是字符串常量池: 为什么在这要把另外两个常量池拿出说一下呢,首先小生我在网上或者cnds上看到很多人在争论字符串常量池是存在与方法区还是堆

  • 浅析Java中Apache BeanUtils和Spring BeanUtils的用法

    # 前言 在我们实际项目开发过程中,我们经常需要将不同的两个对象实例进行属性复制,从而基于源对象的属性信息进行后续操作,而不改变源对象的属性信息,比如DTO数据传输对象和数据对象DO,我们需要将DO对象进行属性复制到DTO,但是对象格式又不一样,所以我们需要编写映射代码将对象中的属性值从一种类型转换成另一种类型. # 对象拷贝 在具体介绍两种 BeanUtils 之前,先来补充一些基础知识.它们两种工具本质上就是对象拷贝工具,而对象拷贝又分为深拷贝和浅拷贝,下面进行详细解释. # 什么是浅拷贝和

  • 浅析Java中的异常处理机制

    异常处理机制 1.抛出异常 2.捕获异常 3.异常处理五个关键字: try.catch.finally.throw.throws 注意:假设要捕获多个异常:需要按照层级关系(异常体系结构) 从小到大! package exception; /** * Java 捕获和抛出异常: * 异常处理机制 * 1.抛出异常 * 2.捕获异常 * 3.异常处理五个关键字 * try.catch.finally.throw.throws * 注意:假设要捕获多个异常:需要按照层级关系(异常体系结构) 从小到大

  • 浅析java中Pair和Map的区别

    在这篇文章中,我们讨论了一个非常有用的编程概念,配对(Pair).配对提供了一种方便方式来处理简单的键值关联,当我们想从方法返回两个值时特别有用. 在核心Java库中可以使用配对(Pair)的实现.除此之外,某些第三方库,比如Apache Commons和Vavr,已经在各自的api中公开了这个功能. 核心java配对实现 Pair类 Pair类在javafx.util 包中,类构造函数有两个参数,键及对应值: Pair<Integer, String> pair = new Pair<

  • 浅析Java中为什么要设计包装类

    目录 一.为什么需要包装类 二.装箱与拆箱 三.不简单的 Integer.valueOf 四.Object 类可以接收所有数据类型 五.包装类在集合中的广泛使用 六.数据类型转换 一.为什么需要包装类 在 Java 中,万物皆对象,所有的操作都要求用对象的形式进行描述.但是 Java 中除了对象(引用类型)还有八大基本类型,它们不是对象.那么,为了把基本类型转换成对象,最简单的做法就是将基本类型作为一个类的属性保存起来,也就是把基本数据类型包装一下,这也就是包装类的由来. 这样,我们先自己实现一

  • 浅析java中常用的定时任务框架-单体

    目录 一.阅读收获 二.本章源码下载 三.Timer+TimerTask 四.ScheduledExecutorService 五.Spring Task 5.1 单线程串行执行-@Scheduled 5.2 多线程并发运行-@Scheduled+配置定时器的程池(推荐) 5.3 多线程并发执行-@Scheduled+@Async+配置异步线程池 5.4 @Scheduled参数解析 六.Quartz 6.1. 创建任务类 6.2. 配置任务描述和触发器 一.阅读收获 1. 了解常用的单体应用定

随机推荐