浅谈Java编程中的内存泄露情况

必须先要了解的
1。c/c++是程序员自己管理内存,Java内存是由GC自动回收的。
我虽然不是很熟悉C++,不过这个应该没有犯常识性错误吧。
2。什么是内存泄露?
内存泄露是指系统中存在无法回收的内存,有时候会造成内存不足或系统崩溃。
在C/C++中分配了内存不释放的情况就是内存泄露。
3。Java存在内存泄露
我们必须先承认这个,才可以接着讨论。虽然Java存在内存泄露,但是基本上不用很关心它,特别是那些对代码本身就不讲究的就更不要去关心这个了。
Java中的内存泄露当然是指:存在无用但是垃圾回收器无法回收的对象。
而且即使有内存泄露问题存在,也不一定会表现出来。
4。Java中参数都是传值的。
对于基本类型,大家基本上没有异议,但是对于引用类型我们也不能有异议。

Java内存泄露情况
 
1、堆内存溢出(outOfMemoryError:java heap space)
       在jvm规范中,堆中的内存是用来生成对象实例和数组的。
       如果细分,堆内存还可以分为年轻代和年老代,年轻代包括一个eden区和两个survivor区。
       当生成新对象时,内存的申请过程如下:
          a、jvm先尝试在eden区分配新建对象所需的内存;
          b、如果内存大小足够,申请结束,否则下一步;
          c、jvm启动youngGC,试图将eden区中不活跃的对象释放掉,释放后若Eden空间仍然不足以放入新对象,则试图将部分Eden中活跃对象放入Survivor区;
          d、Survivor区被用来作为Eden及old的中间交换区域,当OLD区空间足够时,Survivor区的对象会被移到Old区,否则会被保留在Survivor区;
          e、 当OLD区空间不够时,JVM会在OLD区进行full GC;
          f、full GC后,若Survivor及OLD区仍然无法存放从Eden复制过来的部分对象,导致JVM无法在Eden区为新对象创建内存区域,则出现”out of memory错误”:

 outOfMemoryError:java heap space

2、方法区内存溢出(outOfMemoryError:permgem space)
       在jvm规范中,方法区主要存放的是类信息、常量、静态变量等。
       所以如果程序加载的类过多,或者使用反射、gclib等这种动态代理生成类的技术,就可能导致该区发生内存溢出,一般该区发生内存溢出时的错误信息为:

       outOfMemoryError:permgem space

3、线程栈溢出(java。lang。StackOverflowError)
       线程栈时线程独有的一块内存结构,所以线程栈发生问题必定是某个线程运行时产生的错误。
       一般线程栈溢出是由于递归太深或方法调用层级过多导致的。
       发生栈溢出的错误信息为:

       java。lang。StackOverflowError

内存泄露的几种场景:
 
1、长生命周期的对象持有短生命周期对象的引用
 
            这是内存泄露最常见的场景,也是代码设计中经常出现的问题。
            例如:在全局静态map中缓存局部变量,且没有清空操作,随着时间的推移,这个map会越来越大,造成内存泄露。
 
2、修改hashset中对象的参数值,且参数是计算哈希值的字段
 
             当一个对象被存储进HashSet集合中以后,就不能修改这个对象中的那些参与计算哈希值的字段,否则对象修改后的哈希值与最初存储进HashSet集合中时的哈希值就不同了,在这种情况下,即使在contains方法使用该对象的当前引用作为参数去HashSet集合中检索对象,也将返回找不到对象的结果,这也会导致无法从HashSet集合中删除当前对象,造成内存泄露。
 
3、机器的连接数和关闭时间设置
 
            长时间开启非常耗费资源的连接,也会造成内存泄露。

来看个内存泄露的例子:

public class Stack {
 private Object[] elements=new Object[10];
 private int size = 0;

 public void push(Object e){
 ensureCapacity();
 elements[size++] = e;
 }

 public Object pop(){
 if( size == 0)
  throw new EmptyStackException();
 return elements[--size];
 }

 private void ensureCapacity(){
 if(elements。length == size){
  Object[] oldElements = elements;
  elements = new Object[2 * elements。length+1];
  System。arraycopy(oldElements,0, elements, 0, size);
 }
 }
}

上面的原理应该很简单,假如堆栈加了10个元素,然后全部弹出来,虽然堆栈
是空的,没有我们要的东西,但是这是个对象是无法回收的,这个才符合了内存
泄露的两个条件:无用,无法回收。

但是就是存在这样的东西也不一定会导致什么样的后果,如果这个堆栈用的比较少,
也就浪费了几个K内存而已,反正我们的内存都上G了,哪里会有什么影响,再说
这个东西很快就会被回收的,有什么关系。下面看两个例子。
 
例子1

public class Bad{
  public static Stack s=Stack();
  static{
    s。push(new Object());
    s。pop(); //这里有一个对象发生内存泄露
    s。push(new Object()); //上面的对象可以被回收了,等于是自愈了
  }
}

因为是static,就一直存在到程序退出,但是我们也可以看到它有自愈功能,
就是说如果你的Stack最多有100个对象,那么最多也就只有100个对象无法被回收
其实这个应该很容易理解,Stack内部持有100个引用,最坏的情况就是他们都是
无用的,因为我们一旦放新的进取,以前的引用自然消失!
 
例子2

public class NotTooBad{
  public void doSomething(){
    Stack s=new Stack();
    s。push(new Object());
    //other code
    s。pop();//这里同样导致对象无法回收,内存泄露。
  }//退出方法,s自动无效,s可以被回收,Stack内部的引用自然没了,所以
   //这里也可以自愈,而且可以说这个方法不存在内存泄露问题,不过是晚一点
   //交给GC而已,因为它是封闭的,对外不开放,可以说上面的代码99。9999%的
   //情况是不会造成任何影响的,当然你写这样的代码不会有什么坏的影响,但是
   //绝对可以说是垃圾代码!没有矛盾吧,我在里面加一个空的for循环也不会有
   //什么太大的影响吧,你会这么做吗?
}

上面两个例子都不过是小打小闹,但是C/C++中的内存泄露就不是Bad了,而是Worst了。
他们如果一处没有回收就永远无法回收,频繁的调用这个方法内存不就用光了!
因为Java还有自愈功能(我自己起的名字,还没申请专利),所以Java的内存泄露问题
几乎可以忽略了,但是知道的人就不要犯了。

为了避免内存泄露,在编写代码的过程中可以参考下面的建议:
 
1、尽早释放无用对象的引用;
 
2、使用字符串处理,避免使用String,应大量使用StringBuffer,每一个String对象都得独立占用内存一块区域;
 
3、尽量少用静态变量,因为静态变量存放在永久代(方法区),永久代基本不参与垃圾回收;
 
4、避免在循环中创建对象;
 
5、开启大型文件或从数据库一次拿了太多的数据很容易造成内存溢出,所以在这些地方要大概计算一下数据量的最大值是多少,并且设定所需最小及最大的内存空间值。

(0)

相关推荐

  • 详细介绍Java内存泄露原因

    一.Java内存回收机制 不论哪种语言的内存分配方式,都需要返回所分配内存的真实地址,也就是返回一个指针到内存块的首地址.Java中对象是采用new或者反射的方法创建的,这些对象的创建都是在堆(Heap)中分配的,所有对象的回收都是由Java虚拟机通过垃圾回收机制完成的.GC为了能够正确释放对象,会监控每个对象的运行状况,对他们的申请.引用.被引用.赋值等状况进行监控,Java会使用有向图的方法进行管理内存,实时监控对象是否可以达到,如果不可到达,则就将其回收,这样也可以消除引用循环的问题.在J

  • Java中典型的内存泄露问题和解决方法

    Q:在Java中怎么可以产生内存泄露?A:Java中,造成内存泄露的原因有很多种.典型的例子是一个没有实现hasCode和equals方法的Key类在HashMap中保存的情况.最后会生成很多重复的对象.所有的内存泄露最后都会抛出OutOfMemoryError异常,下面通过一段简短的通过无限循环模拟内存泄露的例子说明一下. 复制代码 代码如下: import java.util.HashMap;import java.util.Map; public class MemoryLeak { pu

  • 深入java内存查看与分析详解

    1:gc日志输出在jvm启动参数中加入 -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimestamps -XX:+PrintGCApplicationStopedTime,jvm将会按照这些参数顺序输出gc概要信息,详细信息,gc时间信息,gc造成的应用暂停时间.如果在刚才的参数后面加入参数 -Xloggc:文件路径,gc信息将会输出到指定的文件中.其他参数还有-verbose:gc和-XX:+PrintTenuringDistribution等.

  • java内存溢出示例(堆溢出、栈溢出)

    堆溢出: 复制代码 代码如下: /** * @author LXA * 堆溢出 */ public class Heap { public static void main(String[] args) { ArrayList list=new ArrayList(); while(true) { list.add(new Heap()); } } } 报错: java.lang.OutOfMemoryError: Java heap space 栈溢出: 复制代码 代码如下: /** * @a

  • 基于Java内存溢出的解决方法详解

    一.内存溢出类型1.java.lang.OutOfMemoryError: PermGen spaceJVM管理两种类型的内存,堆和非堆.堆是给开发人员用的上面说的就是,是在JVM启动时创建:非堆是留给JVM自己用的,用来存放类的信息的.它和堆不同,运行期内GC不会释放空间.如果web app用了大量的第三方jar或者应用有太多的class文件而恰好MaxPermSize设置较小,超出了也会导致这块内存的占用过多造成溢出,或者tomcat热部署时侯不会清理前面加载的环境,只会将context更改

  • Java中内存分配的几种方法

    一.数组分配的上限 Java里数组的大小是受限制的,因为它使用的是int类型作为数组下标.这意味着你无法申请超过Integer.MAX_VALUE(2^31-1)大小的数组.这并不是说你申请内存的上限就是2G.你可以申请一个大一点的类型的数组.比如: 复制代码 代码如下: final long[] ar = new long[ Integer.MAX_VALUE ]; 这个会分配16G -8字节,如果你设置的-Xmx参数足够大的话(通常你的堆至少得保留50%以上的空间,也就是说分配16G的内存,

  • 理解Java中的内存泄露及解决方法示例

    本文详细地介绍了Java内存管理的原理,以及内存泄露产生的原因,同时提供了一些列解决Java内存泄露的方案,希望对各位Java开发者有所帮助. Java内存管理机制 在C++ 语言中,如果需要动态分配一块内存,程序员需要负责这块内存的整个生命周期.从申请分配.到使用.再到最后的释放.这样的过程非常灵活,但是却十分繁琐,程序员很容易由于疏忽而忘记释放内存,从而导致内存的泄露. Java 语言对内存管理做了自己的优化,这就是垃圾回收机制. Java 的几乎所有内存对象都是在堆内存上分配(基本数据类型

  • 深入分析Java内存区域的使用详解

    Java 内存划分: 在Java内存分配中,java将内存分为:方法区,堆,虚拟机栈,本地方法栈,程序计数器.其中方法区和堆对于所有线程共享,而虚拟机栈和本地方法栈还有程序计数器对于线程隔离的.每个区域都有各自的创建和销毁时间. 程序计数器:     作用是当前线程所执行的字节吗的行号指示器.Java的多线程是通过线程轮流切换并分配处理器执行时间方式来实现的.因此,每个线程为了能在切换后能恢复到正确的位置,每个线程需要独立的程序计数器. Java 虚拟机栈: 每个放在被执行的时候都会同时创建一个

  • 实例详解Java中ThreadLocal内存泄露

    案例与分析 问题背景 在 Tomcat 中,下面的代码都在 webapp 内,会导致WebappClassLoader泄漏,无法被回收. public class MyCounter { private int count = 0; public void increment() { count++; } public int getCount() { return count; } } public class MyThreadLocal extends ThreadLocal<MyCount

  • 浅谈Java编程中的内存泄露情况

    必须先要了解的 1.c/c++是程序员自己管理内存,Java内存是由GC自动回收的. 我虽然不是很熟悉C++,不过这个应该没有犯常识性错误吧. 2.什么是内存泄露? 内存泄露是指系统中存在无法回收的内存,有时候会造成内存不足或系统崩溃. 在C/C++中分配了内存不释放的情况就是内存泄露. 3.Java存在内存泄露 我们必须先承认这个,才可以接着讨论.虽然Java存在内存泄露,但是基本上不用很关心它,特别是那些对代码本身就不讲究的就更不要去关心这个了. Java中的内存泄露当然是指:存在无用但是垃

  • 浅谈Java编程中string的理解与运用

    一,"=="与equals() 运行以下代码,如何解释其输出结果? public class StringPool { public static void main(String args[]) { String s0="Hello"; String s1="Hello"; String s2="He"+"llo"; System.out.println(s0==s1);//true System.out

  • 浅谈Java编程中的synthetic关键字

    java synthetic关键字.有synthetic标记的field和method是class内部使用的,正常的源代码里不会出现synthetic field.小颖编译工具用的就是jad.所有反编译工具都不能保证完全正确地反编译class.所以你不能要求太多. 下面我给大家介绍一下synthetic 下面的例子是最常见的synthetic field Java代码 class parent { public void foo() { } class inner { inner() { foo

  • 浅谈Java编程中的单例设计模式

    写软件的时候经常需要用到打印日志功能,可以帮助你调试和定位问题,项目上线后还可以帮助你分析数据.但是Java原生带有的System.out.println()方法却很少在真正的项目开发中使用,甚至像findbugs等代码检查工具还会认为使用System.out.println()是一个bug. 为什么作为Java新手神器的System.out.println(),到了真正项目开发当中会被唾弃呢?其实只要细细分析,你就会发现它的很多弊端.比如不可控制,所有的日志都会在项目上线后照常打印,从而降低运

  • 浅谈Java并发中的内存模型

    什么是JavaMemoryModel(JMM)? JMM通过构建一个统一的内存模型来屏蔽掉不同硬件平台和不同操作系统之间的差异,让Java开发者无需关注不同平台之间的差异,达到一次编译,随处运行的目的,这也正是Java的设计目的之一. CPU和内存 在讲JMM之前,我想先和大家聊聊硬件层面的东西.大家应该都知道执行运算操作的CPU本身是不具备存储能力的,它只负责根据指令对传递进来的数据做相应的运算,而数据存储这一任务则交给内存去完成.虽然内存的运行速度虽然比起硬盘快非常多,但是和3GHZ,4GH

  • 浅谈Java继承中的转型及其内存分配

    看书的时候被一段代码能凌乱啦,代码是这样的: package 继承; abstract class People { public String tag = "疯狂Java讲义"; //① public String name = "Parent"; String getName(){ return name; } } class Student extends People { //定义一个私有的tag实例变量来隐藏父类的tag实例变量 String tag =

  • 浅谈Java 类中各成分加载顺序和内存中的存放位置

    一.什么时候会加载类? 使用到类中的内容时加载:有三种情况 1.创建对象:new StaticCode(); 2.使用类中的静态成员:StaticCode.num=9;  StaticCode.show(); 3.在命令行中运行:java StaticCodeDemo 二.类所有内容加载顺序和内存中的存放位置 利用语句进行分析: 1.Person p=new Person("zhangsan",20); 该句话所做的事情: 1.在栈内存中,开辟main函数的空间,建立main函数的变量

  • 浅谈Java编程ToString()方法重写的意义

    上一篇文章我们介绍了java tostring方法重写代码示例,接下来,我们简单聊聊java编程tostring()方法重写的意义. 1.toString()就是是重写,对于一般的对象来说都会有这个方法,其实这个方法的目的,主要就是将对象按字符串的方式输出出来:打个比方,比如一个People类,里边有name,age这两个属性, 如果你People p = new People(); p.toString(); 这么做的话,默认输出就是一个内存地址. 那么你会想到重写ToString():这个方

  • 浅谈Java开发中的安全编码问题

    1 - 输入校验 编码原则:针对各种语言本身的保留字符,做到数据与代码相分离. 1.1 SQL 注入防范 严重性高,可能性低. (1) 参数校验,拦截非法参数(推荐白名单): public String sanitizeUser(String username) { return Pattern.matches("[A-Za-z0-9_]+", username) ? username : "unauthorized user"; } (2) 使用预编译: Stri

  • 浅谈java web中常用对象对应的实例化接口

    1. request对象 是javax.servlet.HttpServletRequest接口的实例化 2. response对象 是javax.servlet.HttpServletResponse接口的实例化 3. session 对象 是javax.servlet.HttpSession接口的实例化 4. application对象 是javax.servlet.ServletContext接口的实例化 以上是常用的对象 5. pageContext对象 是javax.servlet.j

随机推荐