Java垃圾回收机制以及内存泄露

时间:2022-02-03 07:07:42

1、Java的内存泄露介绍

首先明白一下内存泄露的概念:内存泄露是指程序执行过程动态分配了内存,可是在程序结束的时候这块内存没有被释放,从而导致这块内存不可用,这就是内存

泄露,重新启动计算机能够解决问题,可是有可能再次发生内存泄露,内存泄露与硬件没有关系,它是软件设计的缺陷所导致的。

Java发生内存泄露的原因非常明白,就是长声明周期对象持有短声明周期对象的引用就非常可能发生内存泄露。虽然短生命周期对象已经不再须要,可是由于长生命

周期对象在持有它的引用而导致它不能被GC回收,这就是Java内存泄露发生的场景。

java内存泄露场景举例:

当我们不断的向集合类内加入元素,而没有对应的删除机制,导致内存一直被占用,这样也不一定就会造成内存泄露,当该集合类仅仅是一个局部变

量的时候,当方法运行完成退出的时候,自然会被GC所回收,可是怕的是该集合类是一个全局的属性(比方类中的静态变量),那么会导致该集合类占用的内存仅仅

增不减,这样就导致了内存的泄露。所以我们在使用全局性的集合类的时候要注意提供合适的删除策略或者定期清理策略。

内存泄露能够分为4类:

(1)常发性内存泄露:发生内存泄露的代码会多次被运行到,每次运行的时候就会有一块内存泄露

(2)偶发性内存泄露:发生内存泄露的代码仅仅在某些特定环境下才会发生的,常发性与偶发性是相对的,对于特定的环境下,偶发性也就是常发性的。所以,測

试方法和測试环境对检測内存泄露有是非常重要的。

(3)一次性内存泄露:发生内存泄露的代码仅仅会被运行一次,也就内存中总有那么一块内存是不可用的。

(4)隐式内存泄露    :程序运行过程中在不断的分配内存,直到程序结束的时候才会释放全部的内存,严格的说,这里并没有发生内存泄露,由于程序终于释放

了全部申请的内存。可是对于一个server程序来说,往往会连续运行非常多天甚至好几个月的,这样迟早会发生内存溢出的情况。所以,我们称这类内存泄露为隐式

内存泄露。

2、Java内存溢出的问题及解决的方法

JVM管理的内存大致分为三种不同类型的内存区域:

Generation space(永久保存区域)、Heap space(堆区域)、JavaStacks(Java栈)。当中永久保存区域主要存放Class(类)和Meta的信息,Class第一次被Load

的时候被放入PermGenspace区域,Class须要存储的内容主要包含方法和静态属性。堆区域用来存放Class的实例(即对象),对象须要存储的内容主要是非静态

属性。每次用new创建一个对象实例后,对象实例存储在堆区域中,这部分空间也被jvm的垃圾回收机制管理。而Java栈跟大多数编程语言包含汇编语言的栈功能相

似,主要基本类型变量以及方法的输入输出參数。Java程序的每一个线程中都有一个独立的堆栈。easy发生内存溢出问题的内存空间包含:

Permanent Generation space和Heap space。

第一种OutOfMemoryError: PermGenspace

发生这样的问题的原意是程序中使用了大量的jar或class,使java虚拟机装载类的空间不够,与PermanentGeneration
space有关。解决这类问题有下面两种办法:

(1) 添加java虚拟机中的XX:PermSize和XX:MaxPermSize參数的大小,当中XX:PermSize是初始永久保存区域大小,XX:MaxPermSize是最大永久保存区域大

小。如针对tomcat6.0,在catalina.sh或catalina.bat文件里一系列环境变量名说明结束处(大约在70行左右)添加一行:JAVA_OPTS="
-XX:PermSize=64M-

XX:MaxPermSize=128m"假设是windowsserver还能够在系统环境变量中设置。感觉用tomcat公布sprint+struts+hibernate架构的程序时非常easy发生这样的内存溢出

错误。使用上述方法,我成功攻克了部署ssh项目的tomcatserver常常宕机的问题。

(2) 清理应用程序中web-inf/lib下的jar,假设tomcat部署了多个应用,非常多应用都使用了同样的jar,能够将共同的jar移到tomcat共同的lib下,降低类的反复加

载。这样的方法是网上部分人推荐的,我没试过,但感觉降低不了太大的空间,最靠谱的还是第一种方法。

另外一种OutOfMemoryError:  Javaheap space

发生这样的问题的解决办法是java虚拟机创建的对象太多,在进行垃圾回收之间,虚拟机分配的到堆内存空间已经用满了,与Heapspace有关。解决这类问题有两种思

路:

(1)检查程序,看是否有死循环或不必要地反复创建大量对象。找到原因后,改动程序和算法。

我曾经写一个使用K-Means文本聚类算法对几万条文本记录(每条记录的特征向量大约10来个)进行文本聚类时,因为程序细节上有问题,就导致了Javaheap

space的内存溢出问题,后来通过改动程序得到了解决

(2)添加Java虚拟机中Xms(初始堆大小)和Xmx(最大堆大小)參数的大小。如:setJAVA_OPTS=
-Xms256m -Xmx1024m

3、Java的GC机制

一个优秀的程序猿必须了解GC的原理、怎样有变化GC的性能、怎样与GC进行有限的交互,由于一些程序对性能要求较高,比如嵌入式系统、实时系统等,仅仅有

全面提升内存的管理效率,才干有效提升程序的性能。

GC的基本原理:

Java的内存管理实际上就是对象的管理,包含对象的分配与释放

对于程序猿来说,分配对象使用newkeyword,释放对象就是将对象的全部引用赋值为null,让程序不能訪问到这个对象,我们称之为‘不可达’状态,GC负责回收所

有不可达状态的对象的内存空间。

对于GC来说,当程序猿创建了对象,GC就開始监控这个对象的大小、地址、使用情况。通常GC採用有向图的方式来管理堆(heap)中全部对象。通过这样的方式

来确定哪些对象是可达的,哪些对象是不可达的,当GC确定了一些对象是不可达状态的时候,GC就有责任将这些对象回收。可是,为了保证GC可以在不同平台实

现的问题,Java规范对GC的非常多行为都没有进行严格的规定。比如,对于採用什么类型的回收算法、什么时候进行回收等重要问题都没有明白的规定。因此,不

同的JVM的实现者往往有不同的实现算法。

这也给Java程序猿的开发带来行多不确定性。本文研究了几个与GC工作相关的问题,努力降低这样的不确定性给Java程序带来的负面影响。

增量式GC( Incremental GC )

GC在JVM中一般是由一个或一组进程来实现的,它本身也和用户程序一样占用heap空间,执行时也占用CPU.当GC进程执行时,应用程序停止执行。

因此,当GC执行时间较长时,用户可以感到 Java程序的停顿,另外一方面,假设GC执行时间太短,则可能对象回收率太低,这意味着还有非常多应该回收的对象没

有被回收,仍然占用大量内存。因此,在设计GC的时候,就必须在停顿时间和回收率之间进行权衡。一个好的GC实现同意用户定义自己所须要的设置,比如有些内

存有限有设备,对内存的使用量很敏感,希望GC可以准确的回收内存,它并不在意程序速度的放慢。另外一些实时网络游戏,就不可以同意程序有长时间的中

断。增量式GC就是通过一定的回收算法,把一个长时间的中断,划分为非常多个小的中断,通过这样的方式降低GC对用户程序的影响。尽管,增量式GC在总体性能上

可能不如普通GC的效率高,可是它可以降低程序的最长停顿时间。Sun JDK提供的HotSpot JVM就能支持增量式GC.HotSpot JVM缺省GC方式为不使用增量GC,为

了启动增量GC,我们必须在执行Java程序时添加-Xincgc的參数。

HotSpot JVM增量式GC的实现是採用Train GC算法。它的基本想法就是,将堆中的全部对象依照创建和使用情况进行分组(分层),将使用频繁高和具有相关性

的对象放在一队中,随着程序的执行,不断对组进行调整。当GC执行时,它总是先回收最老的(近期非常少訪问的)的对象,假设整组都为可回收对象,GC将整组回

收。这样,每次GC执行仅仅回收一定比例的不可达对象,保证程序的顺畅执行。

具体解释finalize函数

finalize是位于Object类的一个方法,该方法的訪问修饰符为protected,因为全部类为Object的子类,因此用户类非常easy訪问到这种方法。因为,finalize函

数没有自己主动实现链式调用,我们必须手动的实现,因此finalize函数的最后一个语句一般是super.finalize()。

通过这样的方式,我们能够实现从下到上实现finalize的调用,即先释放自己的资源,然后再释放父类的资源。依据Java语言规范,JVM保证调用finalize函数之

前,这个对象是不可达的,可是JVM不保证这个函数一定会被调用。另外,规范还保证finalize函数最多执行一次。非常多Java刚開始学习的人会觉得这种方法类似与C++中

的析构函数,将非常多对象、资源的释放都放在这一函数里面。事实上,这不是一种非常好的方式。原因有三:

其一,GC为了可以支持finalize函数,要对覆盖这个函数的对象作非常多附加的工作。

其二,在finalize执行完毕之后,该对象可能变成可达的,GC还要再检查一次该对象是否是可达的。因此,使用 finalize会减少GC的执行性能。

其三,因为GC调用finalize的时间是不确定的,因此通过这样的方式释放资源也是不确定的。通常,finalize用于一些不easy控制、而且很重要资源的释放,例

如一些I/O的操作,数据的连接。这些资源的释放对整个应用程序是很关键的。在这样的情况下,程序猿应该以通过程序本身管理(包含释放)这些资源为主,以

finalize函数释放资源方式为辅,形成一种双保险的管理机制,而不应该只依靠finalize来释放资源。

Java垃圾回收机制以及内存泄露的更多相关文章

  1. Chrome V8系列--浅析Chrome V8引擎中的垃圾回收机制和内存泄露优化策略

    V8 实现了准确式 GC,GC 算法采用了分代式垃圾回收机制.因此,V8 将内存(堆)分为新生代和老生代两部分. 一.前言 V8的垃圾回收机制:JavaScript使用垃圾回收机制来自动管理内存.垃圾 ...

  2. JavaScript中的垃圾回收机制与内存泄露

    什么是内存泄露? 任何编程语言,在运行时都需要使用到内存,比如在一个函数中, var arr = [1, 2, 3, 4, 5]; 这么一个数组,就需要内存. 但是,在使用了这些内存之后, 如果后面他 ...

  3. Java垃圾回收机制和内存分配

    收集算法是内存回收的方法论,垃圾收集器是内存回收的具体实现 自动内存管理解决的是:给对象分配内存 以及 回收分配给对象的内存 为什么我们要了解学习 GC 与内存分配呢? 在 JVM 自动内存管理机制的 ...

  4. JS基础-垃圾回收机制与内存泄漏的优化

    [V8引擎]浅析Chrome V8引擎中的垃圾回收机制和内存泄露优化策略 垃圾回收机制 如何判断回收内容 如何确定哪些内存需要回收,哪些内存不需要回收,这是垃圾回收期需要解决的最基本问题.我们可以这样 ...

  5. 【转】深入理解 Java 垃圾回收机制

    深入理解 Java 垃圾回收机制   一.垃圾回收机制的意义 Java语言中一个显著的特点就是引入了垃圾回收机制,使c++程序员最头疼的内存管理的问题迎刃而解,它使得Java程序员在编写程序的时候不再 ...

  6. 深入理解java垃圾回收机制

    深入理解java垃圾回收机制---- 一.垃圾回收机制的意义 Java语言中一个显著的特点就是引入了垃圾回收机制,使c++程序员最头疼的内存管理的问题迎刃而解,它使得Java程序员在编写程序的时候不再 ...

  7. java 垃圾回收(堆内存)、以及栈内存的释放

    一.Java的垃圾回收机制———解疑 Java的垃圾回收机制是Java虚拟机提供的能力,用于在空闲时间以不定时的方式动态回收无任何引用的对象占据的内存空间. 需要注意的是:垃圾回收回收的是无任何引用的 ...

  8. java 垃圾回收机制 引用类型

    Java语言的一个重要特性是引入了自动的内存管理机制,使得开发人员不用自己来管理应用中的内存.C/C++开发人员需要通过malloc/free 和new/delete等函数来显式的分配和释放内存.这对 ...

  9. 深入理解 Java 垃圾回收机制

            深入理解 Java 垃圾回收机制 一:垃圾回收机制的意义 java  语言中一个显著的特点就是引入了java回收机制,是c++程序员最头疼的内存管理的问题迎刃而解,它使得java程序员 ...

随机推荐

  1. Oracle分页查询=======之伪列的使用

    ========伪列========== 在Oracle数据库中,伪列不存在表中,但是可以从表中查询到 例如:SELECT ROWID 伪列,tname 教师姓名 FROM teacher; ==== ...

  2. R %operator% 含义

    %foo% is the syntax for a binary operator. In base R: %in%: '"%in%" <- function(x, tabl ...

  3. hdoj 1532 Drainage Ditches【最大流模板题】

    Drainage Ditches Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) ...

  4. 揭开枚举类的面纱(Unlocking the Enumeration&sol;enum Mystery)

    枚举给用户定义固定数据组提供了方便.枚举类就是一系列常量整型值,这也就意味着枚举类型不能被修改. 这里我们将要讨论C语言中枚举类型的用法和限制. 枚举通过枚举关键值定义,类似结构体定义 语法(Synt ...

  5. 《Thinking in Java》学习笔记&lpar;三&rpar;

    1>Java中的常量 使用final和static来修饰的变量称为常量,常量用大写字母表示,字母间用下划线连接. Java中定义常量有以下几种方式: interface ConstantInte ...

  6. MySQL高级知识(十三)——表锁

    前言:锁是计算机协调多个进程或线程并发访问某一资源的机制.在数据库中,除传统的计算机资源(如CPU.RAM.I/O等)的争用外,数据也是一种供许多用户共享的资源.如何保证数据并发访问的一致性.有效性是 ...

  7. Python Web Service

    搞移动端有段时间了,一直使用别人的API,自己也只写过ASP.NET网站作为网络服务,相对来讲是很大的短板.虽然ASP.NET可以提供想要的web服务,但是其体量臃肿,响应速度非常慢,这点我非常不喜欢 ...

  8. PAT A1108 Finding Average (20 分)——字符串,字符串转数字

    The basic task is simple: given N real numbers, you are supposed to calculate their average. But wha ...

  9. ES6学习笔记&lt&semi;一&gt&semi; let const class extends super

    学习参考地址1  学习参考地址2 ECMAScript 6(以下简称ES6)是JavaScript语言的下一代标准.因为当前版本的ES6是在2015年发布的,所以又称ECMAScript 2015:也 ...

  10. vetur插件提示 &lbrack;vue-language-server&rsqb; Elements in iteration expect to have &&num;39&semi;v-bind&colon;key&&num;39&semi; directives

    错误如下图所示: 错误提示: [vue-language-server] Elements in iteration expect to have 'v-bind:key' directives.Re ...