程序员谈话系列————关于jvm的那些事……

jvm这个概念看起来比较高大上,但是每一个想要深入java底层的程序员们都不能绕开这一个点。最近想整理一下自己对于jvm的认知,所以就大概的写一下对于jvm的思考和理解。

说到jvm,不得不提起oom,也就是内存溢出,在说oom之前呢?我们必须要先理清楚另外一个概念,那就是内存泄漏,内存泄漏又是什么呢?

很多没有经验的程序员呢,通常都有一个误区,那就是认为java存在的垃圾自动回收机制可以帮助他们完全不用思考内存管理的事情。这是一个常见的误解:虽然java的垃圾回收器做的已经比较好了,但是即使最好的程序员也有可能成为严重破坏内存泄漏的牺牲品。这是什么意思呢?当我们不必要地维护不再需要的对象引用时,会发生内存泄漏。这些泄漏会造成很大的问题,首先,当程序消耗越来越多的资源的时候,这部分不必要的泄漏会给计算机带来不必要的压力,而且想要检测这些泄漏很困难,常用的静态分析通常很难精确识别这些冗余引用,现有的泄漏检测工具会跟踪和报告有关单个对象的细粒度信息,产生难以解释且缺乏一定精确度的结果。也就是要么难以识别,要么就会使用太过具体且无用的术语来识别。

常见的内存泄漏分为四种Performance(性能):通常和过多的对象创建和删除,垃圾收集的长时间延迟,过多的操作系统页面交换等相关联,Resource constranints(资源约束):当可用内存很少或者内存过于分散而无法分配大对象时,这可能是本机的,或者更常见的是与java堆相关,java heap leaks(java堆泄漏):java对象在不释放的情况下不断创建,这通常是由潜在对象引用引起的,Native memory leaks(本机内存泄漏):与java堆之外的任何不断增长的内存利用率相关联,比如JNI代码或者驱动程序及JVM分配等等。

上面常见的内存泄漏,我用的最多的就是java堆泄漏,所以下面我将介绍一种基于java visualvm报告检测此类泄漏的方法,并利用可视化界面在运行时分析基于java技术的应用程序。

一,内存泄漏和内存溢出(oom)

对于很多人来说,习惯将内存泄漏看成一种疾病,而oom就是这种疾病的一种症状。但是,并非所有的oom都是因为内存泄漏,这就像你腿疼,可能是因为你腿部着凉,也有可能是因为你腿部收到了撞击。当然同样并非所有的内存泄漏都会表现为oom,特别是在没有重新启动时运行很长的情况下。那么什么情况下会内存泄漏会演变成oom呢?程序运行期间泄露的内存块会降低系统的性能,并且分配但未使用的内存块必须在系统耗尽空闲物理内存时进行换出,最终,程序甚至可能耗尽其可用的虚拟地址空间,最终导致了oom。所以oom是内存泄漏的常见指示,当没有足够的空间来分配新对象的时候会抛出oom,当垃圾收集器找不到必要的空间且堆不能够进一步的扩展时,会多次尝试,因此会出现错误已经堆栈跟踪。所以一般想要诊断oom第一步就是确定错误的实际含义,听起来可能很简单,但是还是需要确实人因为java堆已满出现还是因为本机堆已满导致,接下来就让我们分析一下常见的可能错误信息。

二,java.lang.OutOfMemoryError

1.java heap space,此消息不一定意味着内存泄漏,实际上,问题可能与配置问题一样简单。就比如当你的代码种出现一个阵列实例化时,因为需要太多的内存,抛出了这个错误,那么这时不是代码的错误,而是应用程序服务器依赖于默认的堆太小了,可以通过JVM的内存参数来解决这个问题。而在其他的一些情况下,特别是长期存在的应用程序,这个消息可能表明我们无意中持有对象的引用,从而阻止了垃圾收集器来清理它们,这时候Java语言同等于内存泄漏,当然应用程序调用的API也可能无意中持有对象引用。还有另外一种情况是使用finalizers,如果类中具有这个方法,则在垃圾收集时这个类型的对象不会被回收,而是在垃圾收集之后,稍后对象将排队等待最终确定,在Sun实现中,finalizers由守护线程执行,如果finalizers线程无法跟上finalization队列,那么java堆可能会填满并且可能会抛出oom。

2.PermGen space(注意java8不会出现这个问题),永生代已经被填满,永生代是存储类和方法对象的堆的区域,如果应用程序加载了大量类,则可能需要使用-XX:MaxPermSize增加永生代的大小。

3.Requested array size exceeds VM limit,应用程序或者应用程序使用的api正在尝试分配大于堆大小的数组。

4.Requested bytes for.Out of swap space?,虽然看起来像一个OOM,但是当本机堆的分配失败并且本机堆很可能将被耗尽时,HotSpot VM会抛出此异常, 消息中包括失败请求的大小以及内存请求的原因,在大多数的情况下,是报告分配失败的源模块的名称。如果由此类的错误抛出,原因可能有很多,比如操作系统配置的交换空间不足,系统上另一个进程是消耗所有可用的内存资源,另外本机泄漏,应用程序也可能失败。一般需要在操作系统上使用故障排除实用程序来进一步诊断问题。

5. Native method。如果您看到此错误消息并且堆栈跟踪的顶部框架是本机方法,则该本机方法遇到分配失败。此消息与上一个消息之间的区别在于,在JNI或本机方法中检测到Java内存分配失败,而不是在Java VM代码中检测到。如果抛出此类型的OOM,您可能需要在操作系统上使用实用程序来进一步诊断问题。

6. Application Crash Without OOM有时,应用程序可能会在从本机堆分配失败后很快崩溃。如果您运行的本机代码不检查内存分配函数返回的错误,则会发生这种情况。例如,如果没有可用内存,malloc系统调用将返回NULL。如果未检查malloc的返回,则应用程序在尝试访问无效的内存位置时可能会崩溃。根据具体情况,可能很难定位此类问题。在某些情况下,致命错误日志或崩溃转储的信息就足以诊断问题。如果确定崩溃的原因是某些内存分配中缺少错误处理,那么您必须找到所述分配失败的原因。与任何其他本机堆问题一样,系统可能配置了但交换空间不足,另一个进程可能正在消耗所有可用内存资源等。

 以上就是oom常见的几种错误,而当抛出oom时,你要做的就是找到然后解决他,至于方法,咱们下次再说。

posted @ 2020-03-19 12:21  欲码则码  阅读(164)  评论(0编辑  收藏  举报