本文目录一览:
- 1、java是否有内存泄露和内存溢出
- 2、java什么是内存泄漏?
- 3、怎样解决Java中内存泄露
- 4、如何识别Java中的内存泄漏
- 5、Java内存泄露和内存泄露的区别
- 6、java在什么情况下会出现内存泄露
java是否有内存泄露和内存溢出
java中的内存溢出和内存泄漏
内存溢出:
对于整个应用程序来说,JVM内存空间,已经没有多余的空间分配给新的对象。所以就发生内存溢出。
内存泄露:
在应用的整个生命周期内,某个对象一直存在,且对象占用的内存空间越来越大,最终导致JVM内存泄露,
比如:缓存的应用,如果不设置上限的话,缓存的容量可能会一直增长。
静态集合引用,如果该集合存放了无数个对象,随着时间的推移也有可能使容量无限制的增长,最终导致JVM内存泄露。
内存泄露,是应用程序中的某个对象长时间的存活,并且占用空间不断增长,最终导致内存泄露。
是对象分配后,长时间的容量增长。
内存溢出,是针对整个应用程序的所有对象的分配空间不足,会造成内存溢出。
内存泄漏
内存泄漏指由于疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设
计错误,失去了对该段内存的控制,因而造成了内存的浪费。内存泄漏与许多其他问题有着相似的症状,并且通常情况下只能由那些可以获得程序源代码的程序员才
可以分析出来。然而,有不少人习惯于把任何不需要的内存使用的增加描述为内存泄漏,即使严格意义上来说这是不准确的。
一般我们常说的内存泄漏
是指堆内存的泄漏。堆内存是指程序从堆中分配的,大小任意的(内存块的大小可以在程序运行期决定),使用完后必须显示释放的内存。应用程序一般使用
malloc,realloc,new等函数从堆中分配到一块内存,使用完后,程序必须负责相应的调用free或delete释放该内存块,否则,这块内
存就不能被再次使用,我们就说这块内存泄漏了。
内存泄漏可以分为4类:
1.
常发性内存泄漏。发生内存泄漏的代码会被多次执行到,每次被执行的时候都会导致一块内存泄漏。
2.
偶发性内存泄漏。发生内存泄漏的代码只有在某些特定环境或操作过程下才会发生。常发性和偶发性是相对的。对于特定的环境,偶发性的也许就变成了常发性的。所以测试环境和测试方法对检测内存泄漏至关重要。
3.
一次性内存泄漏。发生内存泄漏的代码只会被执行一次,或者由于算法上的缺陷,导致总会有一块仅且一块内存发生泄漏。比如,在类的构造函数中分配内存,在析构函数中却没有释放该内存,所以内存泄漏只会发生一次。
4.
隐式内存泄漏。程序在运行过程中不停的分配内存,但是直到结束的时候才释放内存。严格的说这里并没有发生内存泄漏,因为最终程序释放了所有申请的内存。但
是对于一个服务器程序,需要运行几天,几周甚至几个月,不及时释放内存也可能导致最终耗尽系统的所有内存。所以,我们称这类内存泄漏为隐式内存泄漏。
简单点:
内存泄漏就是忘记释放使用完毕的内存,让下次使用有一定风险。
内存溢出就是一定的内存空间不能装下所有的需要存放的数据,造成内存数据溢出。
主要从以下几部分来说明,关于内存和内存泄露、溢出的概念,区分内存泄露和内存溢出;内存的区域划分,了解GC回收机制;重点关注如何去监控和发现内存问题;此外分析出问题还要如何解决内存问题。
下面就开始本篇的内容:
第一部分 概念
众所周知,java中的内存由java虚拟机自己去管理的,他不像C++需要自己去释放。笼统地
去讲,java的内存分配分为两个部分,一个是数据堆,一个是栈。程序在运行的时候一般分配数据堆,把局部的临时的变量都放进去,生命周期和进程有关系。
但是如果程序员声明了static的变量,就直接在栈中运行的,进程销毁了,不一定会销毁static变量。
另外为了保证java内存不会溢出,java中有垃圾回收机制。
System.gc()即垃圾收集机制是指jvm用于释放那些不再使用的对象所占用的内存。java语言并不要求jvm有gc,也没有规定gc如何工作。垃圾收集的目的在于清除不再使用的对象。gc通过确定对象是否被活动对象引用来确定是否收集该对象。
而其中,内存溢出就是你要求分配的java虚拟机内存超出了系统能给你的,系统不能满足需求,于是产生溢出。
内存泄漏是指你向系统申请分配内存进行使用(new),可是使用完了以后却不归还(delete),结果你申请到的那块内存你自己也不能再访
问,该块已分配出来的内存也无法再使用,随着服务器内存的不断消耗,而无法使用的内存越来越多,系统也不能再次将它分配给需要的程序,产生泄露。一直下
去,程序也逐渐无内存使用,就会溢出。
第二部分 原理
JAVA垃圾回收及对内存区划分
在Java虚拟机规范中,提及了如下几种类型的内存空间:
◇ 栈内存(Stack):每个线程私有的。
◇ 堆内存(Heap):所有线程公用的。
◇ 方法区(Method Area):有点像以前常说的“进程代码段”,这里面存放了每个加载类的反射信息、类函数的代码、编译时常量等信息。
◇ 原生方法栈(Native Method Stack):主要用于JNI中的原生代码,平时很少涉及。
而Java的使用的是堆内存,java堆是一个运行时数据区,类的实例(对象)从中分配空间。Java虚拟机(JVM)的堆中储存着正在运行的应用程序所建立的所有对象,“垃圾回收”也是主要是和堆内存(Heap)有关。
垃圾回收的概念就是JAVA虚拟机(JVM)回收那些不再被引用的对象内存的过程。一般我们认为正在被引用的对象状态为“alive”,而没有
被应用或者取不到引用属性的对象状态为“dead”。垃圾回收是一个释放处于”dead”状态的对象的内存的过程。而垃圾回收的规则和算法被动态的作用于
应用运行当中,自动回收。
JVM的垃圾回收器采用的是一种分代(generational )回收策略,用较高的频率对年轻的对象(young
generation)进行扫描和回收,这种叫做minor collection,而对老对象(old generation)的检查回收频率要低很多,称为major
collection。这样就不需要每次GC都将内存中所有对象都检查一遍,这种策略有利于实时观察和回收。
(Sun JVM 1.3
有两种最基本的内存收集方式:一种称为copying或scavenge,将所有仍然生存的对象搬到另外一块内存后,整块内存就可回收。这种方法有效率,但需要有一定的空闲内存,拷贝也有开销。这种方法用于minor
collection。另外一种称为mark-compact,将活着的对象标记出来,然后搬迁到一起连成大块的内存,其他内存就可以回收了。这种方法不需要占用额外的空间,但速度相对慢一些。这种方法用于major collection.
)
一些对象被创建出来只是拥有短暂的生命周期,比如 iterators 和本地变量。另外一些对象被创建是拥有很长的生命周期,比如持久化对象等。
垃圾回收器的分代策略是把内存区划分为几个代,然后为每个代分配一到多个内存区块。当其中一个代用完了分配给他的内存后,JVM会在分配的内存区内执行一个局部的GC(也可以叫minor
collection)操作,为了回收处于“dead”状态的对象所占用的内存。局部GC通常要比Full GC快很多。
JVM定义了两个代,年轻代(yong generation)(有时称为“nursery”托儿所)和老年代(old generation)。年轻代包括
“Eden space(伊甸园)”和两个“survivor spaces”。虚拟内存初始化的时候会把所有对象都分配到 Eden
space,并且大部分对象也会在该区域被释放。 当进行 minor GC的时候,VM会把剩下的没有释放的对象从Eden space移动到其中一个survivor
spaces当中。此外,VM也会把那些长期存活在survivor spaces 里的对象移动到 老生代的“tenured” space中。当 tenured
generation 被填满后,就会产生Full GC,Full GC会相对比较慢因为回收的内容包括了所有的 live状态的对象。pemanet
generation这个代包括了所有java虚拟机自身使用的相对比较稳定的数据对象,比如类和对象方法等。
关于代的划分,可以从下图中获得一个概况:
第三部分 总结
内存溢出主要是由于代码编写时对某些方法、类应用不合理,或者没有预估到临时对象会占用很大内存量,或者把过多的数据放入JVM缓存,或者性能
压力大导致消息堆积而占用内存,以至于在性能测试时,生成庞大数量的临时对象,GC时没有做出有效回收甚至根本就不能回收,造成内存空间不足,内存溢出。
如果编码之前,对内存使用量进行预估,对放在内存中的数据进行评估,保证有用的信息尽快释放,无用的信息能够被GC回收,这样在一定程度上是可以避免内存溢出问题的。
java什么是内存泄漏?
内存泄露是指无用对象(不再使用的对象)持续占有内存或无用对象的内存得不到及时释放,
从而造成的内存空间的浪费
长生命周期的对象持有短生命周期对象的引用就很可能发生内存泄露,
尽管短生命周期对象已经不再需要,
但是因为长生命周期对象持有它的引用而导致不能被回收,
这就是java中内存泄露的发生场景
内存溢出,内存溢出是超出可使用的最大范围了。比如系统给你分配1G内存,你用完了,还想用,系统不给你分,就溢出了
怎样解决Java中内存泄露
一旦知道确实发生了内存泄漏,就需要更专业的工具来查明为什么会发生泄漏。JVM自己是不会告诉您的。这些专业工具从JVM获得内存系统信息的方法基本上有两种:JVMTI和字节码技术(byte code instrumentation)。Java虚拟机工具接口(Java Virtual Machine Tools Interface,JVMTI)及其前身Java虚拟机监视程序接口(Java Virtual Machine Profiling Interface,JVMPI)是外部工具与JVM通信并从JVM收集信息的标准化接口。字节码技术是指使用探测器处理字节码以获得工具所需的信息的技术。
Optimizeit是Borland公司的产品,主要用于协助对软件系统进行代码优化和故障诊断,其中的Optimizeit Profiler主要用于内存泄漏的分析。Profiler的堆视图就是用来观察系统运行使用的内存大小和各个类的实例分配的个数的。
首先,Profiler会进行趋势分析,找出是哪个类的对象在泄漏。系统运行长时间后可以得到四个内存快照。对这四个内存快照进行综合分析,如果每一次快照的内存使用都比上一次有增长,可以认定系统存在内存泄漏,找出在四个快照中实例个数都保持增长的类,这些类可以初步被认定为存在泄漏。通过数据收集和初步分析,可以得出初步结论:系统是否存在内存泄漏和哪些对象存在泄漏(被泄漏)。
接下来,看看有哪些其他的类与泄漏的类的对象相关联。前面已经谈到Java中的内存泄漏就是无用的对象保持,简单地说就是因为编码的错误导致了一条本来不应该存在的引用链的存在(从而导致了被引用的对象无法释放),因此内存泄漏分析的任务就是找出这条多余的引用链,并找到其形成的原因。查看对象分配到哪里是很有用的。同时只知道它们如何与其他对象相关联(即哪些对象引用了它们)是不够的,关于它们在何处创建的信息也很有用。
最后,进一步研究单个对象,看看它们是如何互相关联的。借助于Profiler工具,应用程序中的代码可以在分配时进行动态添加,以创建堆栈跟踪。也有可以对系统中所有对象分配进行动态的堆栈跟踪。这些堆栈跟踪可以在工具中进行累积和分析。对每个被泄漏的实例对象,必然存在一条从某个牵引对象出发到达该对象的引用链。处于堆栈空间的牵引对象在被从栈中弹出后就失去其牵引的能力,变为非牵引对象。因此,在长时间的运行后,被泄露的对象基本上都是被作为类的静态变量的牵引对象牵引。
总而言之, Java虽然有自动回收管理内存的功能,但内存泄漏也是不容忽视,它往往是破坏系统稳定性的重要因素。
如何识别Java中的内存泄漏
一般来说内存泄漏有两种情况。一种情况,在堆中的分配的内存,在没有将其释放掉的时候,就将所有能访问这块内存的方式都删掉(如指针重新赋值);另一种情况则是在内存对象明明已经不需要的时候,还仍然保留着这块内存和它的访问方式(引用)。第一种情况,在Java中已经由于垃圾回收机制的引入,得到了很好的解决。所以,Java中的内存泄漏,主要指的是第二种情况。
可能光说概念太抽象了,大家可以看一下这样的例子:
1
Vector
v=new
Vector(10);
2
for
(int
i=1;i100;
i++){
3
Object
o=new
Object();
4
v.add(o);
5
o=null;
6
}
在这个例子中,代码栈中存在Vector对象的引用v和Object对象的引用o。在For循环中,我们不断的生成新的对象,然后将其添加到Vector对象中,之后将o引用置空。问题是当o引用被置空后,如果发生GC,我们创建的Object对象是否能够被GC回收呢?答案是否定的。因为,GC在跟踪代码栈中的引用时,会发现v引用,而继续往下跟踪,就会发现v引用指向的内存空间中又存在指向Object对象的引用。也就是说尽管o引用已经被置空,但是Object对象仍然存在其他的引用,是可以被访问到的,所以GC无法将其释放掉。如果在此循环之后,Object对象对程序已经没有任何作用,那么我们就认为此Java程序发生了内存泄漏。
尽管对于C/C++中的内存泄露情况来说,Java内存泄露导致的破坏性小,除了少数情况会出现程序崩溃的情况外,大多数情况下程序仍然能正常运行。但是,在移动设备对于内存和CPU都有较严格的限制的情况下,Java的内存溢出会导致程序效率低下、占用大量不需要的内存等问题。这将导致整个机器性能变差,严重的也会引起抛出OutOfMemoryError,导致程序崩溃。
Java内存泄露和内存泄露的区别
答:Java内存泄露与溢出的区别:
内存溢出就是你要求分配的内存超出了系统能给你的,系统不能满足需求,于是产生溢出。ava内存泄漏就是没有及时清理内存垃圾,导致系统无法再给你提供内存资源(内存资源耗尽)。看到上面的解释,可能有些朋友还是不太理解吧。没问题,看以下例子
Java内存泄露是说程序逻辑问题,造成申请的内存无法释放.这样的话无论多少内存,早晚都会被占用光的.最简单的例子就是死循环了.由于程序判断错误导经常发生此事
Java内存泄漏是指在堆上分配的内存没有被释放,从而失去对其控制。这样会造成程序能使用的内存越来越少,导致系统运行速度减慢,严重情况会使程序当掉。
关于内存溢出有点出入。比如说你申请了一个integer,但给它存了long才能存下的数,那就是内存溢出。
举个现实中的例子:比如有一个桶,装满了水.你丢个苹果进去。桶的水正常。如果你放个大石头。水就出溢出,内存溢出也就是这个原理。
区别:内存溢出,提供的内存不够;Java内存泄漏,无法再提供内存资源
java在什么情况下会出现内存泄露
内存泄露就是指一个不再被程序使用的对象或变量一直被占据在内存中。java中的内存泄露的情况:
1.长生命周期的对象持有短生命周期对象的引用就很可能发生内存泄露,例如,缓存系统,我们加载了一个对象放在缓存中(例如放在一个全局map对象中),然后一直不再使用它,这个对象一直被缓存引用,但却不再被使用。
2.集合类,而如果集合类是全局性的变量(比如类中的静态属性,全局性的map等即有静态引用或final一直指向它),那么没有相应的删除机制,很可能导致集合所占用的内存只增不减,因此提供这样的删除机制或者定期清除策略非常必要。
3.单例模式。不正确使用单例模式是引起内存泄露的一个常见问题,单例对象在被初始化后将在JVM的整个生命周期中存在(以静态变量的方式),如果单例对象持有外部对象的引用,那么这个外部对象将不能被jvm正常回收,导致内存泄露