十年网站开发经验 + 多家企业客户 + 靠谱的建站团队
量身定制 + 运营维护+专业推广+无忧售后,网站问题一站解决
内存泄漏示例
成都创新互联从2013年创立,是专业互联网技术服务公司,拥有项目网站设计制作、成都做网站网站策划,项目实施与项目整合能力。我们以让每一个梦想脱颖而出为使命,1280元勐腊做网站,已为上家服务,为勐腊各地企业和个人服务,联系电话:18982081108
在这个例子中,循环申请Object 对象,并将所申请的对象放入一个Vector 中,如果仅仅释放引用本身,那么Vector 仍然引用该对象,所以这个对象对GC 来说是不可回收的。因此,如果对象加入到Vector 后,还必须从Vector 中删除,最简单的方法就是将Vector对象设置为null。
Vector v = new Vector(10);
for (int i = 1; i100; i++){
Object o = new Object();
v.add(o);
o = null;
}// 此时,所有的Object 对象都没有被释放,因为变量v 引用这些对象。实际上无用,而还被引用的对象,GC 就无能为力了(事实上GC 认为它还有用),这一点是导致内存泄漏最重要的原因。
内存泄漏可以分为4类: 1. 常发性内存泄漏。发生内存泄漏的代码会被多次执行到,每次被执行的时候都会导致一块内存泄漏。 2. 偶发性内存泄漏。发生内存泄漏的代码只有在某些特定环境或操作过程下才会发生。常发性和偶发性是相对的。对于特定的环境,偶发性的也许就变成了常发性的。所以测试环境和测试方法对检测内存泄漏至关重要。 3. 一次性内存泄漏。发生内存泄漏的代码只会被执行一次,或者由于算法上的缺陷,导致总会有一块仅且一块内存发生泄漏。比如,在类的构造函数中分配内存,在析构函数中却没有释放该内存,所以内存泄漏只会发生一次。 4. 隐式内存泄漏。程序在运行过程中不停的分配内存,但是直到结束的时候才释放内存。严格的说这里并没有发生内存泄漏,因为最终程序释放了所有申请的内存。但是对于一个服务器程序,需要运行几天,几周甚至几个月,不及时释放内存也可能导致最终耗尽系统的所有内存。所以,我们称这类内存泄漏为隐式内存泄漏。 从用户使用程序的角度来看,内存泄漏本身不会产生什么危害,作为一般的用户,根本感觉不到内存泄漏的存在
内存溢出是指应用系统中存在无法回收的内存或使用的内存过多,最终使得程序运行要用到的内存大于虚拟机能提供的最大内存。
所以我们应该明确:存在内存溢出的因不一定导致内存溢出的果
public class Know {
public static void main(String [] args)
{
int w = new Integer(args[0]).intValue();
int h = Integer.parseInt(args[1]);
for(int i=0;ih;i++)
{
StringBuffer sb=new StringBuffer();
for(int j=0;iw;j++)
{
sb.append('*');
}
System.out.println(sb.toString());
}
}
}
这是我在网上找的一个例子,试验了一下,是对的,造成内存溢出的原因是
for(int j=0;iw;j++)
{
sb.append('*');
}
是死循环,我原先是这么写的一个例子
public class Know {
public static void main(String[] args) {
while(true){
System.out.println("ok");
}
}
}
但并没有导致内存溢出,应该是它消耗的内存比较小或者运行时间短,正如这句话所说“存在内存溢出的因不一定导致内存溢出的果”
希望可以帮到你
前些天紧急出版本,发现一个内存泄漏问题,程序每次注销都会有大量的对象没有释放,在
注销登陆成功后,又会重新生成一个相同的对象。这样,在做界面自动化测试的过程中,系统
频繁注销,登陆,再注销。这样如此反复多次,会必然导致java这个进程的内存溢出OutOfMemory。
拿到问题,用JProfile把程序跑起来,查到具体泄漏的对象,然后进行详细的分析。
发现两个地方存在严重的泄漏:
1 某对象在创建,初始化的过程会创建一个线程,这个线程专门处理和计算机串口的通讯。
代码如下:package example;/*** @author zLan1028** TODO To change the template for this generated type comment go to
* Window - Preferences - Java - Code Style - Code Templates*/public class MemoeryTest{
public static void main(String[] args){MemoeryTest test = new MemoeryTest();
test.init();
test = null;
//do other something}
public void init(){thread.start();}
private Thread thread = new BasicThread ();
boolean exit = false;
class BasicThread extends Thread(){List lstData = new LinkedList();
public void run(){while(!exit){synchronized(lstData){if (lstData.isEmpty()){lstData.wait();}writeDataToSerialPort((String)lstData.removeFirst());}}}
咋一看,应该会,这个对象已经没有其它引用存在。其实不然,因为这个对象中创建了一个内部类。
而在java中,内部类会自动获得一个对外部类的对象引用,而在释放test对象时,没有去把它内部创建的线程对象,
所以这个线程对象在程序退出前会一直存在,所以它会一直保持对外部test对象的引用,这样,每创建一个MemoeryTest
对象,都会存在一个线程对象泄漏,而且一个MemoeryTest对象泄漏。这样时非常危险的。
如果只对这个对象进行初始化,而在程序注销时没有对创建的线程资源回收,则这个问题可以避免。
代码如下:在MemoeryTest 对象中增加一个 public void close()方法,在每次释放MemoeryTest 对象时,主动调用close方法释放资源。
自己改一下下面的代码,把堆栈中的元素改成mp3类型的或更大点的东西
4.Java中参数都是传值的。
对于基本类型,大家基本上没有异议,但是对于引用类型我们也不能有异议。
Java内存泄露情况
JVM回收算法 是很复杂的,我也不知道他们怎么实现的,但是我只知道他们要实现的就是:对于没有被引用的对象是可以回收的。所以你要造成内存泄露就要做到:
持有对无用对象的引用!
不要以为这个很轻易做到,既然无用,你怎么还会持有它的引用? 既然你还持有它,它怎么会是无用的呢?
以下以堆栈更经典这个经典的例子来剖析。
Java代码
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
Java代码
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
Java代码
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循环也不会有
//什么太大的影响吧,你会这么做吗?
}
第一阶段 通过jdk的GC输出进行测试
可以在 JAVA_OPTS增加以下参数打开jdk的GC输出日志:
-verbose:gc -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError
打开输出日志,jdk会在每一次的垃圾回收时打印相关日志
第二阶段 通过jmap命令
jmap命令可以获得运行中的jvm的堆的快照,从而可以离线分析堆,以检查内存泄漏,检查一些严重影响性能的大对象的创建,检查系统中什么对象最多,各种对象所占内存的大小等等
第三阶段 通过Eclipse Memory Analyzer 分析工具来分析
Eclipse Memory Analyzer是一种快速的,功能丰富的Java堆分析工具,以下简称MAT,可以帮助查找内存泄露,并减少内存消耗。 这个工具可以对由堆转储产生的数以亿计的对象进行分析,一旦堆转储被解析,可以在打开他的一瞬间,立即得到保留大小的单一对象,提取记录详细的信息,查看为什么这些对象对象资料没有被释放掉。使用这些功能的报告,可以对这些对象进行跟踪,找到内存泄露嫌疑人,也可以得到系统的性能指数,帮助优化系统。