Java java中会存在内存泄漏漏吗

你使用Rxjava时,内存泄漏了吗? - 简书
你使用Rxjava时,内存泄漏了吗?
今天有位同学问了我一个问题,话说,问我
“有遇到网络请求一半,退出Activity造成的Theard泄露吗?已在销毁时调用了un了
我去查看了下rx的源码的unsubscribe方法,定位到一个实现类,NewThreadWorker的unsubscribe方法中,源码如下:
public void unsubscribe() {
isUnsubscribed =
executor.shutdownNow();
deregisterExecutor(executor);
public boolean isUnsubscribed() {
return isU
这个shutdownNow()在java的注释中写的很清楚
There are no guarantees beyond best-effort attempts to stop* processing actively executing tasks.
public interface ExecutorService extends Executor {
* Initiates an orderly shutdown in which previously submitted
* tasks are executed, but no new tasks will be accepted.
* Invocation has no additional effect if already shut down.
* &p&This method does not wait for previously submitted tasks to
* complete execution.
Use {@link #awaitTermination awaitTermination}
* to do that.
void shutdown();
* Attempts to stop all actively executing tasks, halts the
* processing of waiting tasks, and returns a list of the tasks
* that were awaiting execution.
* &p&This method does not wait for actively executing tasks to
* terminate.
Use {@link #awaitTermination awaitTermination} to
* do that.
* &p&There are no guarantees beyond best-effort attempts to stop
* processing actively executing tasks.
For example, typical
* implementations will cancel via {@link Thread#interrupt}, so any
* task that fails to respond to interrupts may never terminate.
* @return list of tasks that never commenced execution
List&Runnable& shutdownNow();
它只是尽量保证停止所有tasks,因为,如果耗时操作没有做完,finished掉activity,同时unsubscribe 掉Subscription的话,可能还有后台线程在做一些耗时任务。那么会不会造成内存泄露呢?
我觉得还是要源码来说说话吧
* Unsubscribe from all of the subscriptions in the list, which stops the receipt of notifications on
* the associated {@code Subscriber}.
public void unsubscribe() {
if (!unsubscribed) {
List&Subscription&
synchronized (this) {
if (unsubscribed) {
unsubscribed =
subscriptions =
// we will only get here once
unsubscribeFromAll(list);
private static void unsubscribeFromAll(Collection&Subscription& subscriptions) {
if (subscriptions == null) {
List&Throwable& es =
for (Subscription s : subscriptions) {
s.unsubscribe();
} catch (Throwable e) {
if (es == null) {
es = new ArrayList&Throwable&();
es.add(e);
Exceptions.throwIfAny(es);
实际上会做一些清除引用,暂停任务的操作。因此,一般来讲在activity的ondestroy中调用unsubscribe之后,是不会造成内存泄露的。但是真的是这样的吗?让我们来做做实验吧,结果说明一切。
为了能够更好的证明这一点,我还特意做了一个app demo去验证。
主要代码:
secondActivity.png
耗时任务rx封装
demo地址已经放到了github上:
启动app,首先进入的是MainActivity,然后,我们进入SecondActivity这时候,onresume执行,我们的任务也就开始了,稍微过几秒,我们退出SecondActivity,回到MainActivity,这之后,显然,SecondActivity的ondestory方法会被执行,我们可以发现日志也停止了打印。
耗时任务正在执行
如上图所示,停留在了5就不在执行了。
这时候,我们GC一下,在导出hprof文件,注意,为什么要手动GC一下呢?因为android虚拟机去GC也是有策略的,有GC周期的。这时候可能并没有GC过,也就是说,SecondActivity的内存可能并没有被释放,但并不等于说,SecondActivity就泄露了,因为他也有可能是可以被GC的,只是还没有来得及被GC而已。总之,在导出hprof文件之前,最好先手动GC一下。就是下图那个车子,嗯,点一下吧,放心点。
然后熟悉查看hprof文件的同学这时候可能就看到了,执行分析之后,并没有看到内存泄露。
检查内存泄漏神器.png
从上图我们看到SecondeActivity已经是红色,标明被回收了,不存在内存泄漏。
同时,我调试跟踪了一下unsubscribe调用栈,因为是一堆抽象类及接口,又有一堆的实现类,所以,最效率的方法还是调试跟踪,这时候路径就出来了,具体怎么个调发请看我的手稿,比较粗糙。
最终发现,最后的根源就是hander 的 removeCallbacksAndMessages 方法被调用了。
unsubscribe
removeCallbacksAndMessages.png
因此是不存在内存泄漏的,是这样的吗??让我们来在看一个例子!
假如耗时任务本来就是一个异步任务呢?
跑几秒钟,然后回到MainActivity,这时候你在GC一下,hprof文件导出看看,我去,泄漏了。
真的泄漏了
在看看控制台,我去,一直执行到跑完。
一直跑完了
然后等跑完了,在GC一下,在导出hprof文件看看,内存泄漏依然还在,SecondActivity永久放入了你的内存,知道APP进程死掉才会释放。
不信的话,可以多启动SecondActivity,退出SecondActivity几次 ,你会发现内存不断飙升~~
内存不断飙升
我继续在思考,是不是这个耗时任务本身就丢在一个线程中执行,所以,如果我们rx不切换线程,是不是就不会泄露呢?
所以,还是不服,在改改代码,继续~
rx不切换线程了,反正是在非主线程做耗时操作
结果,并没有什么卵用,和上述情况一致,多次启动、关闭SecondActivity ,你会发现内存一样会飙升,GC后,导出hprof文件看看,一样泄露了了。
所以,大家应该懂了使用 rx的正确知识,自己的任务都同步写,线程切换交给Rx,因为Rx更懂你~~。
I write because it is a habit, a passion.
内存管理的目的就是让我们在开发中怎么有效的避免我们的应用出现内存泄漏的问题。内存泄漏大家都不陌生了,简单粗俗的讲,就是该被释放的对象没有释放,一直被某个或某些实例所持有却不再被使用导致 GC 不能回收。最近自己阅读了大量相关的文档资料,打算做个总结沉淀下来跟大家一起分享和学...
上篇博客我们写到了 Java/Android 内存的分配以及相关 GC 的详细分析,这篇博客我们会继续分析 Android 中内存泄漏的检测以及相关案例,和 Android 的内存优化相关内容。上篇:Android 性能优化之内存泄漏检测以及内存优化(上)。中篇:Andro...
在 Java 中,内存的分配是由程序完成的,而内存的释放则是由 Garbage Collecation(GC) 完成的,Java/Android 程序员不用像 C/C++ 程序员一样手动调用相关函数来管理内存的分配和释放,虽然方便了很多,但是这也就造成了内存泄漏的可能性,所...
作者:小强 贝聊移动开发部 Android工程师 1.Java内存分配策略 Java 程序运行时的内存分配策略有三种:静态分配、栈式分配和堆式分配。对应的存储区域如下: 静态存储区(方法区):主要存放静态数据、全局 static 数据和常量。这块内存在程序编译时就已经分配好...
人与人之间的差别天差地别。 绝大多数的普通人,思考质量本来就低下,执行力又不足,难免终生碌碌无为。 我越来越觉得,独立思考的重要性。身边大多数的人都只是人云亦云,所谓的思考大多是没有真正的独立思考,即使思考了也不过是浅层的表面思考。 脑袋决定命运。 正确的投资是美好人生的助...
一、准备工作 服务器:最好是多台,大于等于2 已经搭建好的zookeeper集群 下载软件kafka_2.11-0.10.0.1.tgz 二、创建目录 三、修改配置文件 进入到config目录 主要关注:server.properties 这个文件即可,我们可以发现在目录下...
今日打折事件,真的是服了自己,用脚趾头想一下都应该知道的结果,确实死活都转不过弯过来,从此我知道了一个道理,不要随便彰显你的“无知”,真的是煮熟的鸭子都飞了,要是自己的客户还好,是同事的,是捅了多大的篓子啊!无论是对公司还是别人,都造成了不良的印象,真的是应该要找打啊! 其...
去年趁着电影节的时候看了末代皇帝,影片拍摄背景就不介绍了,观影不多,也不太懂,就从观感聊聊。片子的整体颜色都很暗沉,可以理解,大凡冠上末代啥啥的,都不太合适出现太过明快的颜色,哪怕是代表皇权的明黄。 全片英语,最容易听懂的就是那句“open the door”,有溥仪说的,...
说起石井四郎,大家可能不太熟悉但说起另一个名字,你们无人不知731部队。没错他就是骇人听闻731部队的创建者。他阴险狠毒抗战时期他带领他的细菌部队驻扎在东北哈尔滨附近开始了他的细菌大屠杀。
一站结束后,有37个国家签署了关于在战争中禁止使用细菌作为武器的文件其中就...下次自动登录
现在的位置:
& 综合 & 正文
JAVA内存泄漏原因和内存泄漏检测工具
虚拟机(JVM)及其垃圾收
集器(garbage collector,GC)负责管理大多数的内存任务,
软件程序中还是有可能出现内
存泄漏。实际上,这在大型项目中是一个常见的问题。避免内存泄漏的第一步是要弄清楚它是如何发生的。本文介绍了编写Java代码的一些常见的内存泄漏陷
阱,以及编写不泄漏代码的一些最佳实践。一旦发生了内存泄漏,要指出造成泄漏的代码是非常困难的。因此本文还介绍了一种新工具,用来诊断泄漏并指出根本原
因。该工具的开销非常小,因此可以使用它来寻找处于生产中的系统的内存泄漏。
垃圾收集器的作用
  虽然垃圾收集器处理了大多数内存管理问题,从而使编程人员的生活变得更轻松了,但是编程人员还是
可能犯错而导致出现内存问题。简单地说,GC循环地跟踪所有来自“根”对象(堆栈对象、静态对象、JNI句柄指向的对象,诸如此类)的引用,并将所有它所
能到达的对象标记为活动的。程序只可以操纵这些对象;其他的对象都被删除了。因为GC使程序不可能到达已被删除的对象,这么做就是
  虽然内存管理可以说是自动化的,但是这并不能使编程人员免受思考内存管理问题之苦。例如,分配
(以及释放)内存总会有开销,虽然这种开销对编程人员来说是不可见的。创建了太多对象的程序将会比完成同样的功能而创建的对象却比较少的程序更慢一些(在
其他条件相同的情况下)。
  而且,与本文更为密切相关的是,如果忘记“释放”先前分配的内存,就可能造成内存泄漏。如果程序
保留对永远不再使用的对象的引用,这些对象将会占用并耗尽内存,这是因为自动化的垃圾收集器无法证明这些对象将不再使用。正如我们先前所说的,如果存在一
个对对象的引用,对象就被定义为活动的,因此不能删除。为了确保能回收对象占用的内存,编程人员必须确保该对象不能到达。这通常是通过将对象字段设置为
null或者从集合(collection)中移除对象而完成的。但是,注意,当局部变量不再使用时,没有必要将其显式地设置为null。对这些变量的引
用将随着方法的退出而自动清除。
  概括地说,这就是内存托管语言中的内存泄漏产生的主要原因:保留下来却永远不再使用的对象引用。
  既然我们知道了在Java中确实有可能发生内存泄漏,就让我们来看一些典型的内存泄漏及其原因。
  在大的应用程序中有某种全局的数据储存库是很常见的,例如一个JNDI树或一个会话表。在这些情
况下,必须注意管理储存库的大小。必须有某种机制从储存库中移除不再需要的数据。
  这可能有多种方法,但是最常见的一种是周期性运行的某种清除任务。该任务将验证储存库中的数据,
并移除任何不再需要的数据。
  另一种管理储存库的方法是使用反向链接(referrer)计数。然后集合负责统计集合中每个入
口的反向链接的数目。这要求反向链接告诉集合何时会退出入口。当反向链接数目为零时,该元素就可以从集合中移除了。
  缓存是一种数据结构,用于快速查找已经执行的操作的结果。因此,如果一个操作执行起来很慢,对于
常用的输入数据,就可以将操作的结果缓存,并在下次调用该操作时使用缓存的数据。
  缓存通常都是以动态方式实现的,其中新的结果是在执行时添加到缓存中的。典型的是:
检查结果是否在缓存中,如果在,就返回结果。
如果结果不在缓存中,就进行计算。
计算出来的结果添加到缓存中,以便以后对该操作的调用可以使用。
  该算法的问题(或者说是潜在的内存泄漏)出在最后一步。如果调用该操作时有
相当多的不同输入,就将有相当多的结果
在缓存中。很明显这不是正确的方法。
  为了预防这种具有潜在破坏性的设计,程序必须确保对于缓存所使用的内存容量有一个上限。因此,更
好的算法是:
检查结果是否在缓存中,如果在,就返回结果。
如果结果不在缓存中,就进行计算。
果缓存所占的空间过大,就移除缓存最久的结果。
将计算出来的结果添加到缓存中,以便以后对该操作的调用可以使用。
  通过始终移除缓
存最久的结果,我们实际上进行了这样的假设:在将来,比起缓存最久的数据,最近输入的数据更有可能用到。这通常是一个不错的假设。
  新算法将确保缓存的容量处于预定义的内存范围之内。确切的范围可能很难计算,因为缓存中的对象在
不断变化,而且它们的引用包罗万象。为缓存设置正确的大小是一项非常复杂的任务,需要将所使用的内存容量与检索数据的速度加以平衡。
  解决这个问题的另一种方法是使用java.lang.ref.SoftReference类跟踪
缓存中的对象。这种方法保证这些引用能够被移除,如果虚拟机的内存用尽而需要更多堆的话。
ClassLoader
ClassLoader结构的使用为内存泄漏提供了许多可乘之机。正是该结构本身的复杂性使ClassLoader在内存泄漏方面存在如此多的问题。
ClassLoader的特别之处在于它不仅涉及“常规”的对象引用,还涉及元对象引用,比如:字段、方法和类。这意味着只要有对字段、方法、类或
ClassLoader的对象的引用,ClassLoader就会驻留在JVM中。因为ClassLoader本身可以关联许多类及其静态字段,所以就有
许多内存被泄漏了。
确定泄漏的位置
  通常发生内存泄漏的第一个迹象是:在应用程序中出现了OutOfMemoryError。这通常
发生在您最不愿意它发生的生产环境中,此时几乎不能进行调试。有可能是因为
环境运行应用程序的方式与
生产系统不完全相同,因而导致泄漏只出现在生产中。在这种情况下,需要使用一些开销较低的工具来监控和查找内存泄漏。还需要能够无需重启系统或修改代码就
可以将这些工具连接到正在运行的系统上。可能最重要的是,当进行分析时,需要能够断开工具而保持系统不受干扰。
  虽然OutOfMemoryError通常都是内存泄漏的信号,但是也有可能应用程序确实正在使
用这么多的内存;对于后者,或者必须增加JVM可用的堆的数量,或者对应用程序进行某种更改,使它使用较少的内存。但是,在许多情况
下,OutOfMemoryError都是内存泄漏的信号。一种查明方法是不间断地监控GC的活动,确定内存使用量是否随着时间增加。如果确实如此,就可
能发生了内存泄漏。
  有许多监控垃圾收集器活动的方法。而其中使用最广泛的可能是使用-Xverbose:gc选项启
动JVM,并观察输出。
[memory ] 10.109-10.235: GC 65536K-&16788K
(65536K), 126.000 ms
 箭头后面的值(本例中是16788K)是垃圾收集所使用的堆的容量。
  查看连续不断的GC的详细统计信息的输出将是非常乏味的。幸好有这方面的工具。JRockit
Management Console可以显示堆使用量的图示。借助于该图,可以很容易地看出堆使用量是否随时间增加。
Figure 1. The
JRockit Management Console
甚至可以配置该管理控制台,以便如果发生堆使用量过大的情况(或基于其他的事件),控制台能够向您发
送电子邮件。这明显使内存泄漏的查看变得更容易了。
内存泄漏检测工具
  还有其他的专门进行内存泄漏检测的工具。JRockit Memory Leak
Detector可以用来查看内存泄漏,并可以更深入地查出泄漏的根源。这个强大的工具是紧密集成到JRockit
JVM中的,其开销非常小,对虚拟机的堆的访问也很容易。
专业工具的优点
  一旦知道确实发生了内存泄漏,就需要更专业的工具来查明为什么会发生泄漏。JVM自己是不会告诉
您的。这些专业工具从JVM获得内存系统信息的方法基本上有两种:JVMTI和字节码技术(byte code
instrumentation)。Java虚拟机工具接口(Java Virtual Machine Tools
Interface,JVMTI)及其前身Java虚拟机监视程序接口(Java Virtual Machine Profiling
Interface,JVMPI)是外部工具与JVM通信并从JVM收集信息的标准化接口。字节码技术是指使用探测器处理字节码以获得工具所需的信息的技
  对于内存泄漏检测来说,这两种技术有两个缺点,这使它们不太适合用于生产环境。首先,它们在内存
占用和性能降低方面的开销不可忽略。有关堆使用量的信息必须以某种方式从JVM导出,并收集到工具中进行处理。这意味着要为工具分配内存。信息的导出也影
响了JVM的性能。例如,当收集信息时,垃圾收集器将运行得比较慢。另外一个缺点是需要始终将工具连在JVM上。这是不可能的:将工具连在一个已经启动的
JVM上,进行分析,断开工具,并保持JVM运行。
  因为JRockit Memory Leak
Detector是集成到JVM中的,就没有这两个缺点了。首先,许多处理和分析工作是在JVM内部进行的,所以没有必要转换或重新创建任何数据。处理还
可以背负(piggyback)在垃圾收集器本身上而进行,这意味着提高了速度。其次,只要JVM是使用-Xmanagement选项(允许通过远程
JMX接口监控和管理JVM)启动的,Memory Leak
Detector就可以与运行中的JVM进行连接或断开。当该工具断开时,没有任何东西遗留在JVM中,JVM又将以全速运行代码,正如工具连接之前一
  让我们深入地研究一下该工具以及它是如何用来跟踪内存泄漏的。在知道发生内存泄漏之后,第一步是
要弄清楚泄漏了什么数据--哪个类的对象引起了泄漏?JRockit Memory Leak
Detector是通过在每次垃圾收集时计算每个类的现有对象的数目来实现这一步的。如果特定类的对象数目随时间而增长(“增长率”),就可能发生了内存
图2. Memory
Leak Detector的趋势分析视图
  因为泄漏可能像细流一样非常小,所以趋势分析必须运行很长一段时间。在短时间内,可能会发生一些
类的局部增长,而之后它们又会跌落。但是趋势分析的开销很小(最大开销也不过是在每次垃圾收集时将数据包由JRockit发送到Memory Leak
Detector)。开销不应该成为任何系统的问题——即使是一个全速运行的生产中的系统。
  起初数目会跳跃不停,但是一段时间之后它们就会稳定下来,并显示出哪些类的数目在增长。
找出根本原因
  有时候知道是哪些类的对象在泄漏就足以说明问题了。这些类可能只用于代码中的非常有限的部分,对
代码进行一次快速检查就可以显示出问题所在。遗憾地是,很有可能只有这类信息还并不够。例如,常见到泄漏出在类java.lang.String的对象
上,但是因为字符串在整个程序中都使用,所以这并没有多大帮助。
  我们想知道的是,另外还有哪些对象与泄漏对象关联?在本例中是String。为什么泄漏的对象还
存在?哪些对象保留了对这些对象的引用?但是能列出的所有保留对String的引用的对象将会非常多,以至于没有什么实际用处。为了限制数据的数量,可以
将数据按类分组,以便可以看出其他哪些对象的类与泄漏对象(String)关联。例如,String在Hashtable中是很常见的,因此我们可能会看
到与String关联的Hashtable数据项对象。由Hashtable数据项倒推,我们最终可以找到与这些数据项有关的Hashtable对象以及
String(如图3所示)。
在工具中看到的类型图的示例视图
  因为我们仍然是以类的对象而不是单独的对象来看待对象,所以我们不知道是哪个Hashtable
在泄漏。如果我们可以弄清楚系统中所有的Hashtable都有多大,我们就可以假定最大的Hashtable就是正在泄漏的那一个(因为随着时间的流逝
它会累积泄漏而增长得相当大)。因此,一份有关所有Hashtable对象以及它们引用了多少数据的列表,将会帮助我们指出造成泄漏的确切
Hashtabl。
界面:Hashtable对象以及它们所引用数据的数量的列表
  对对象引用数据数目的计算开销非常大(需要以该对象作为根遍历引用图),如果必须对许多对象都这
么做,将会花很多时间。如果了解一点Hashtable的内部实现原理就可以找到一条捷径。Hashtable的内部有一个Hashtable数据项的数
组。该数组随着Hashtable中对象数目的增长而增长。因此,为找出最大的Hashtable,我们只需找出引用Hashtable数据项的最大数
组。这样要快很多。
界面:最大的Hashtable数据项数组及其大小的清单
  当找到发生泄漏的Hashtable实例时,我们可以看到其他哪些实例在引用该
Hashtable,并倒推回去看看是哪个Hashtable在泄漏。
这就是工具中的实例图
  例如,该Hashtable可能是由MyServer类型的对象在名为
activeSessions的字段中引用的。这种信息通常就足以查找源代码以定位问题所在了。
检查对象以及它对其他对象的引用
找出分配位置
  当跟踪内存泄漏问题时,查看对象分配到哪里是很有用的。只知道它们如何与其他对象相关联(即哪些
对象引用了它们)是不够的,关于它们在何处创建的信息也很有用。当然了,您并不想创建应用程序的辅助构件,以打印每次分配的堆栈跟踪(stack
trace)。您也不想仅仅为了跟踪内存泄漏而在运行应用程序时将一个分析程序连接到生产环境中。
  借助于JRockit Memory Leak
Detector,应用程序中的代码可以在分配时进行动态添加,以创建堆栈跟踪。这些堆栈跟踪可以在工具中进行累积和分析。只要不启用就不会因该功能而产
生成本,这意味着随时可以进行分配跟踪。当请求分配跟踪时,JRockit
编译器动态插入代码以监控分配,但是只针对所请求的特定类。更好的是,在进行数据分析时,添加的代码全部被移除,代码中没有留下任何会引起应用程序性能降
低的更改。
示例程序执行期间String的分配的堆栈跟踪
  内存泄漏是难以发现的。本文重点介绍了几种避免内存泄漏的最佳实践,包括要始终记住在数据结构中
所放置的内容,以及密切监控内存使用量以发现突然的增长。
  我们都已经看到了JRockit Memory Leak
Detector是如何用于生产中的系统以跟踪内存泄漏的。该工具使用一种三步式的方法来找出泄漏。首先,进行趋势分析,找出是哪个类的对象在泄漏。接下
来,看看有哪些其他的类与泄漏的类的对象相关联。最后,进一步研究单个对象,看看它们是如何互相关联的。也有可能对系统中所有对象分配进行动态的堆栈跟
踪。这些功能以及该工具紧密集成到JVM中的特性使您可以以一种
而强大的方式跟踪内存泄
漏并进行修复。
【上篇】【下篇】推荐这篇日记的豆列
&&&&&&&&&&&&内存泄漏是指在系统中已经失去作用的对象,仍然占有着系统的内存空间,未能被回收,释放。
C++中产生的内存泄漏是因为未能在使用对象完成之后,及时的调用析构函数释放空间。
java中是因为采用的不恰当的操作导致jvm的GC机制不能及时的发现垃圾,并且回收他,导致无用的对象一直在系统中存在。比如不合理的使用静态集合类导致集合类中的数据不能被及时的回收。
在java中为了能够及时的使无用的对象被及时回收,应该在对象不会使用时,及时的将对线的引用置为空。在C++中,应该及时的使用析构函数释放对象。
内存泄漏是程序动态分配的堆内存未释放或者是无法释放,导致程序运行速度变慢,资源浪费。
c++ 的内存泄漏是手动申请内存 然后没有释放掉,java会有垃圾回收,没有被垃圾回收就会发生内存泄漏。
在手动申请完之后,记得有手动释放。
内存泄漏就是指堆内存中的对象或者数组没有引用或者指针指向了,并且无法被回收,永久的存在于内存空间之中。
c++没有垃圾回收机制,因此对于申请的内存需要手动的释放,如果没有释放,并且没有指正指向之后,就会造成内存泄漏。
java有垃圾回收机制,当对象或者数组没有引用指向的时候,就会被垃圾回收器回收掉,所以一般java内存泄漏比c++要少一些,但是有一种情况就可以造成java内存泄漏。a.b = new b();
b.a = new b()这种情况,引用不存在于栈中,无法使用,也无法回收,这样就会造成内存泄漏。
这道题你会答吗?花几分钟告诉大家答案吧!
扫描二维码,关注牛客网
下载牛客APP,随时随地刷题
京ICP备号-4
扫一扫,把题目装进口袋发布于 05/28 17:07
1 内存泄漏的背景知识
为了判断 Java 中是否有内存泄漏,我们首先必须了解 Java 是如何管理内存的。下面我们先给出一个简单的内存泄漏的例子,在这个例子中我们循环申请 Object 对象,并将所申请的对象放入一个 HashMap 中,如果我们仅仅释放引用本身,那么 HashMap 仍然引用该对象,所以这个对象对 GC 来说是不可回收的。
& 1 2 3 4 5 6 7 8
& HashMap mapObj = new HashMap() public void myfun() { String obj1 = new String("abcd"); mapObj.put(obj1, obj1); ... obj1 = //此时 obj1 指向的物理内存没有释放,因为 hashmap 引用该地址 }
JVM 可以自动回收垃圾,但它只能回收满足条件的垃圾,有时需要们确保条件的满足。如果程序中,存在越来越多不在影响程序未来执行的对象(也就是不再需要的对象),而且这些对象和根对象之间存在引用路径,那么就发生了内存泄漏。
内存泄漏常发生在如下场景:
全局容器类,对象不再需要时,忘记从容器中 remove
像 Runnable 对象等被 Java 虚拟机自身管理的对象,没有正确的释放渠道。Runnable 对象必须交给一个 Thread 去 run,否则该对象就永远不会消亡
1.1 Java 对象的 Size
在 64 位的平台上,Java 对象的占用内存如下
1.2 对象及其引用
为了说明对象和引用,我们先定义一个简单的类
& class Person { S
Person p1 = new Person() 包含如下几个动作
右边的 new Person 在堆空间分配一块内存,创建一个 Person 类对象
末尾的 () 意味着创建对象之后,立即调用构造函数,进行初始化
左边的 Person p1 创建了一个引用变量,所谓引用变量,就是后来用于指向 Person 类示例的引用
= 符号使刚刚创建的对象引用指向刚刚创建的对象 上面的代码如下所示:
如果再将对象赋值给 p2 的话,变成下面这样的
执行 p2 = new Person() 之后变成
1.3 虚拟机垃圾自动回收机制
垃圾自动回收做两件事情:
标记过程现在主要使用 根可达性 分析(还有引用计数法等),清除之后可能会有一些小的内存快,所有还有压缩的过程。
下图中的灰色对象表示可以被回收的对象(根不可达)
哪些对象可以成为根呢?
没有被任何外部对象引用的栈上的对象
JNI handler 包括全局和局部
系统 Class
存活着的监视器
2 内存泄漏的症状
2.1 为什么会发生 OOM 问题?
内存不足会有三种情况:
对内存不足
本地内存不足
Perm 内存不足
发生 OOM 的时候,可以检查如下几个方面:
应用程序的缓存功能
大量长期活动对象
对内存泄漏
本地内存泄漏
2.2 内存泄漏的症状
内存泄漏一般会有如下几个症状:
系统越来越慢,并且有 CPU 使用率过高
运行一段时间后,OOM
虚拟机 core dump
3 内存泄漏的定位和分析
内存泄漏的分析并不复杂,但需要耐心,一般内存泄漏只能事后分析,而重现问题需要耐心。
3.1 对内存泄漏定位
当出现 java.lang.OutOfMemoryError: Java Heap Space 异常,就表示堆内存不足了。堆内存不足的原因有如下几种:
堆内存设置太小
设计不足,缓存了多余的数据
如果怀疑有内存泄漏,可以添加 -verbose:gc 参数后重现启动 Java 进程,输出大致如下:
& 1 2 3 4 5 6 7 8 9 10
& : [GC 164675K-&77056K), 0.0117749 secs] # 表示垃圾回收的时间点,秒为单位。GC/Full GC 表示垃圾回收的类型 : [Full GC 251016K-&77056K), 0.8142190 secs] # 251016K表示回收前占用的内存大小,164654K 表示回收后占用的内存大小,1277056K 表示当前对内存总大小,0.8142190 表示回收耗时 : [GC 164678K-&77248K), 0.0123627 secs] : [Full GC 251214K-&77248K), 0.8135393 secs] : [GC 164700K-&77376K), 0.0130357 secs] : [Full GC 251285K-&77376K), 0.8118171 secs] : [GC 164726K-&77568K), 0.0121369 secs]
: [Full GC 251182K-&77568K), 0.8186925 secs] : [GC 164766K-&77760K), 0.0123415 secs] : [Full GC 251028K-&77760K), 0.8144430 secs]
怀疑内存泄漏后,我们通过 Full GC 日志进一步确认,检查 Full GC 后的可用内存是否持续增大。步骤如下:
获取系统稳定后的 GC 日志(不稳定的日志不可靠)
过滤 FullGC 日志,可能会有如下两种情况
FullGC 后内存使用量持续增长,一直到设置的堆内存最大值,基本可以确定内存泄漏
内存使用量增长后又回落,出于一个动态平衡区间,基本排除内存泄漏
GC 日志只能帮忙找到是否有泄漏,找出内存泄漏的地方,需要依赖一些其他的工具
OptimizedIt
-Xrunhprof
3.2 本地内存泄漏的定位
GC 日志无异常,但 Java 进程使用内存逐渐增大,并且无停止上涨的趋势。本地内存泄漏的原因有如下几个:
JNI 调用中出现内存泄漏(JNI 调用出现内存泄漏,可以使用 C/C++ 内存泄漏分析方法定位)
操作系统问题
本地内存泄漏可能伴有如下异常
& java.lang.OutOfMemoryError: unable to create new native thread , Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread at java.lang.Thread.start0(Native Method) at java.lang.Thread.start(Thread.java:574) at TestThread.main(TestThread.java:34)
上面这个异常可能的原因有:
创建的线程过多,可打印总线程数查看
swap 分区不足
堆内存过大,本地内存不足
3.3 Perm 区内存不足定位
出现 java.lang.OutOfMemoryError: PermGen space Perm ,说明 Perm 区内存不足
依赖注入,没有卸载
Perm 区太小
3.4 分析方法
jmap -histo&& objhist.log,
如果系统已经 OutOfMemory,可以使用 jmap-heap:format=b&获取内存信息
添加参数 -XX:+HeapDumpOnOutOfMemoryError-XX:HeapDumpPath=”$PATH” 当发生 OOM 时会收集相应信息
& 著作权归作者所有
人打赏支持
码字总数 119502
最近在看《深入理解Java虚拟机:JVM高级特性与最佳实践》(第二版)这本书,理论+实践结合,深入浅出,强烈推荐给大家。 这两天在“小怪的java群”里面也对JVM内容进行了一个讨论,讨论的内容...
小怪聊职场 ? 05/31 ?
很多运行在Java虚拟机(JVM)中的应用,包括数据服务如Apache Spark和Kafka以及传统企业应用,都运行在容器中。最近,运行在容器里的JVM出现了由于内存和CPU资源限制和使用率导致性能损失问题...
java高级架构牛人 ? 06/04 ?
概述 前几天在一个群里看到一个朋友发了一个demo,说是JDK的bug,昨天在JVM的一个群里又有朋友发了,觉得挺有意思,分享给大家,希望大家升级JDK的版本的时候注意下是否存在这样的代码,如果...
你假笨 ? 06/06 ?
一、Java的堆内存和栈内存 Java把内存划分成两种:一种是堆内存,一种是栈内存。 堆:主要用于存储实例化的对象,数组。由JVM动态分配内存空间。一个JVM只有一个堆内存,线程是可以共享数据的...
光明辉煌 ? 05/21 ?
磁盘已满   导致系统无法正常运行的最可能的原因是磁盘已满。一个好的网络管理员会密切关注磁盘的使用情况,隔一定的时间,就需要将磁盘上的一些负载转存到备份存储介质中(例如磁带)。 ...
Tar0 ? 今天 ?
Java堆和栈的区别和介绍以及JVM的堆和栈 一、Java的堆内存和栈内存 Java把内存划分成两种:一种是堆内存,一种是栈内存。 堆:主要用于存储实例化的对象,数组。由JVM动态分配内存空间。一个...
代金券优惠 ? 05/24 ?
前言: 在遨游了一番 Java Web 的世界之后,发现了自己的一些缺失,所以就着一篇深度好文:知名互联网公司校招 Java 开发岗面试知识点解析 ,来好好的对 Java 知识点进行复习和学习一番,大部...
我没有三颗心脏 ? 05/16 ?
jvm原理 Java虚拟机是整个java平台的基石,是java技术实现硬件无关和操作系统无关的关键环节,是java语言生成极小体积的编译代码的运行平台,是保护用户机器免受恶意代码侵袭的保护屏障。JVM...
烂猪皮 ? 05/08 ?
庞彤彤 2014年8月加入去哪儿,目前就职于大住宿事业部,主要负责交易运营相关内容。 一、问题 线上的 task 服务出现报警,没有服务者,发现有的机器突然下线了。 第一次出现问题时,发现服务...
Qunar技术沙龙 ? 06/12 ?
转载声明:本文为DBA+社群原创文章,转载必须连同本订阅号二维码全文转载,并注明作者名字及来源:DBA+社群(dbaplus)。 做为一个IT运维人员,通常在运维过程中会遇到各种各样的问题,系统问...
没有更多内容
加载失败,请刷新页面
目录 方法一:直接使用已知的cookie访问 方法二:模拟登录后再携带得到的cookie访问 方法三:模拟登录后用session保持登录状态 方法四:使用无头浏览器访问 正文 方法一:直接使用已知的coo...
steel7c4 ? 28分钟前 ?
服务器为外部配置(名称值对或等效的YAML内容)提供了基于资源的HTTP。服务器可以使用@EnableConfigServer注释轻松嵌入到Spring Boot应用程序中。所以这个应用程序是一个配置服务器: Config...
明理萝 ? 29分钟前 ?
当我在 使用python 的 os.system(r'"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe"') 来打开应用的时候,第一次 执行的时候,没有问题,多次或者再次执行可能就会出现下面的...
之渊 ? 30分钟前 ?
flyed ? 33分钟前 ?
OpenGLES是OpenGL在Android平台上的实现。 变换流程包括:物体坐标系-&世界坐标系-&摄像机坐标系-&裁剪空间-&标准设备空间-&实际窗口空间 这里面有时会用坐标系,有时会用空间,这主要是侧重...
国仔饼 ? 36分钟前 ?
1. 定义理解 当你想使用一个已经存在的类,而它的接口不符合你的需求,或者你想创建一个可重用的类(与不兼容接口无关的类),这时候可以考虑使用适配器模式。同时它也是一种包装模式,它与装...
liuyan_lc ? 37分钟前 ?
Tomcat介绍: 目前有很多网站是用java编写的,所以解析Java程序就必须有相关的软件来完成,Tomcat就是其中之一。 Tomcat是Apache软件基金会(Apache Software Foundation)的Jakarta项目中的...
laoba ? 37分钟前 ?
imperative 英[?m'per?t?v] 美[?m'p?r?t?v] adj. 势在必行的; 必要的,不可避免的; 命令的,专横的; [语] 祈使的; n. 命令; 必要的事; 规则; [语] 祈使语气;...
writeademo ? 38分钟前 ?
SPDY 是 Google 开发的基于传输控制协议 (TCP) 的应用层协议 ,开发组正在推动 SPDY 成为正式标准(现为互联网草案)。SPDY 协议旨在通过压缩、多路复用和优先级来缩短网页的加载时间和提高安...
China_OS ? 39分钟前 ?
Mahout协同过滤 1.Mahout是什么 Mahout是一个算法库,集成了很多算法Apache Mahout是Apache Software Foundation(ASF)旗下的一个开源项目,提供一些可拓展的机器学习领域经典算法的实现,...
xiaomin0322 ? 40分钟前 ?
没有更多内容
加载失败,请刷新页面
文章删除后无法恢复,确定取消删除此文章吗?
亲,自荐的博客将通过私信方式通知管理员,优秀的博客文章审核通过后将在博客推荐列表中显示
确定推荐此文章吗?
确定推荐此博主吗?
聚合全网技术文章,根据你的阅读喜好进行个性推荐
指定官方社区
深圳市奥思网络科技有限公司版权所有}

我要回帖

更多关于 java中存在内存泄漏 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信