最近,每个人都对内存监视以进行性能测试非常感兴趣。有很多关于内存泄漏的文章。
我刚刚完成了一个项目的性能测试,并且“幸运的是”还遇到了内存泄漏的情况,所以我想在这里与您分享。
主要从以下几部分来解释内存和内存泄漏和溢出的概念,区分内存泄漏和内存溢出;划分内存区域,了解GC恢复机制;专注于如何监视和查找内存问题;另外,分析是否有问题,如何解决内存问题。
从下面开始本文的内容
第一部分概念
众所周知,Java中的内存是由Java虚拟机本身管理的,他不希望C ++自己释放它。一般来说,Java的内存分配分为两部分,一个是数据堆,另一个是堆栈。程序运行时,通常会分配数据堆,并将局部临时变量放入其中。生命周期与过程有关。但是,如果程序员声明一个静态变量,它将直接在堆栈上运行,并且进程将被破坏,并且该静态变量不一定会被破坏。
此外,为了确保Java内存不会溢出,java中提供了一种垃圾回收机制。 System.gc()是垃圾回收机制,是指用于释放不再使用的对象占用的内存的jvm。 Java语言不需要jvm具有gc,也没有规定gc的工作方式。垃圾收集的目的是删除不再使用的对象。 gc通过确定活动对象是否引用了该对象来确定是否收集该对象。
其中,内存溢出意味着您请求分配的Java虚拟机的内存超出了系统可以提供的容量,并且系统无法满足需求,因此发生了溢出。
内存泄漏是指您向系统申请分配使用的内存(新),但在使用后不将其返回(删除)。结果,您将无法再访问您申请的内存,并且该块已分配。内存无法再使用。随着服务器内存的不断消耗以及越来越多的不可用内存,系统无法再次将其分配给所需的程序,从而导致泄漏。随着程序的继续,程序逐渐耗尽内存并溢出。
第二部分原则
JAVA垃圾回收和内存区域划分
在Java虚拟机规范中,提到了以下类型的内存空间:
◇堆栈内存(Stack):每个线程专用。
◇堆内存(Heap):所有线程共有。
◇方法区(MethodArea):有点像前面经常提到的“过程代码段”,其中存储了每个已加载类的反射信息,类函数的代码,编译时常量和其他信息。

◇本机方法栈(Native Method Stack):主要用于JNI中的本机代码,通常很少涉及。
Java使用堆内存。 Java堆是运行时数据区,类的实例(对象)从该数据区分配空间。 Java虚拟机(JVM)的堆存储了正在运行的应用程序创建的所有对象。 “垃圾回收”也主要与堆内存(Heap)有关。
垃圾收集的概念是Java虚拟机(JVM)回收那些不再引用的对象的内存的过程。通常,我们认为被引用对象的状态为“活动”,而未应用或无法获取被引用属性的对象的状态为“死”。垃圾回收是释放处于“死”状态的对象的内存的过程。垃圾回收的规则和算法会动态地应用到应用程序中,并会自动进行收集。
JVM的垃圾收集器采用世代收集策略,该策略以较高的频率扫描和收集年轻对象(年轻一代)。这称为次要收集,而旧对象(oldgeneration))检查和收集的频率要低得多,这称为主要收集。这消除了每个GC检查内存中所有对象的需要。该策略有利于实时观察和恢复。
(Sun JVM 1. 3有两种基本的内存收集方法:一种称为复制或清除,将所有仍然存在的对象移动到另一块内存后,可以回收整个内存。这种内存该方法高效,但需要一定数量的可用内存,并且复制也有开销,此方法用于次要收集,另一种方法称为mark-compact,用于标记活动对象,然后将它们一起移动到一个对象中。内存大块。其他内存可以回收。此方法不需要额外的空间,但是速度相对较慢。此方法用于主要收集。)
某些对象的创建周期很短,例如迭代器和局部变量。
创建的其他对象的生命周期较长,例如高度持久的对象。
垃圾收集器的生成策略是将内存区域划分为几代,然后为每一代分配一个或多个内存块。当其中一个代理用完分配给他的内存时,JVM将在分配的内存区域中执行本地GC(也称为次要收集)操作,以便回收处于“死”状态的对象所占用的内存。本地GC通常比完整GC快得多。
JVM定义了两代,即年轻一代(永代)(有时称为“苗圃”托儿所)和老一代。年轻一代包括“伊甸园空间”和两个“幸存者空间”。初始化虚拟内存后,所有对象都将分配到Eden空间,并且大多数对象也将在此区域释放。执行次要GC时,VM会将剩余的未释放对象从Edenspace移动到幸存者空间之一。此外,VM还将幸存者空间中的那些长期存在的对象移动到旧一代的“永久”空间中。当使用期限生成时,将生成FullGC。完全GC相对较慢,因为恢复的内容包括所有处于活动状态的对象。 Pemanetgeneration包含Java虚拟机本身使用的所有相对稳定的数据对象,例如类和对象方法。
关于世代划分,可以从下图获得概述:
如果垃圾收集器影响系统的性能或成为系统的瓶颈,则可以通过自定义每一代的大小来优化其性能。使用JConsole,您可以轻松查看当前应用程序配置的垃圾收集器的各种参数。有关更详细的参数,您可以参考以下调优介绍:
使用5. 0 HotSpot VM调整垃圾回收
最后,总结每个区域的内存:

Eden Space(堆):最初,内存已分配给该线程池中的大多数对象。
Survivor Space(堆):用于将垃圾回收后尚未回收的对象保存在eden空间内存池中。
持久性生成(堆):用于保留在生存空间存储池中存在一段时间的对象。
永久生成(非堆):保存虚拟机自身的静态(恢复性)数据,例如类和方法对象。 Java虚拟机共享这些类型的数据。该区域分为只读和只写
代码缓存(非堆):HotSpot Java虚拟机包括一个用于编译和保存本机代码的内存,称为“代码缓存”
第三部分监视(工具发现问题)
当涉及到内存监视工具时,必须引入JConsole。它是用JAVA编写的GUI程序,用于监视VM和监视远程VM。它易于使用且功能强大。具体来说,它可以监视JAVA内存,JAVA CPU使用率,线程执行,加载类概述等。Jconsole需要在JVM参数中配置端口以使用它。
因为它是一个GUI程序,所以该界面是可视化的,因此在这里我将不对其进行详细介绍。
有关特定的帮助和支持文档,请参阅性能测试JConsole用法摘要:
性能测试辅助工具-如何使用JConsole.aspx
或参考SUN官方网站上的技术文档:
实际测试某个项目时,发生了内存泄漏。最初,在1小时的性能测试中并不明显,但是在稳定性测试期间,发现应用程序的HSF调用在运行数小时后性能明显下降。服务日志中报告了许多HSF超时,但是呼叫系统没有任何超时日志,并且压力应用程序的负载非常低。在检查日志之后,可以认为该应用程序可能存在内存泄漏。通过对jconsole和jmap工具的分析,发现确实存在内存泄漏问题,其中PS OldGen最终占据了100%的占用率。如图所示:
从上图可以看出,尽管每次FullGC都将部分回收JVM内存,但回收未完成,并且将有越来越多的不可回收内存对象,因此将出现上述趋势。当越来越多的对象无法由FullGC回收时,所使用的内存最终将达到系统分配的最大内存,并且系统最终将没有分配的内存,并且计算机最终将关闭。
第四部分分析
由架构师开发和分析应用程序之后,此时检查内存队列以查看哪个对象占用最多的数据,然后使用jmap命令分析线程数据,如下所示:
num#instances#bytes类名称
————————————————
1:92482 com.taobao.matrix.mc.domain。**
2:92482 com.taobao.matrix。**
3:92488 java.util。**
4:0664 java.util.Date
前三个实例继续增加,涉及相同的代码逻辑,异步分发问题,消息阻塞以及多次回收失败。导致内存溢出。
此外,该应用程序的性能是单独测试的,其性能只能支持大约一半。因此,TPS发送消息时,应用程序必须无法处理它,从而导致消息累积,并且JAVA垃圾回收期认为这些都是有用的对象,导致累积内存,直到系统崩溃为止。
调整方法
由于特定的调整方法涉及应用程序的配置信息,因此暂时不在此处列出。您可以参考性能测试团队发布的“性能测试调优合集”
第四部分摘要
内存溢出主要是由于在编写代码时某些方法和类的不合理应用,或者不能估计临时对象会占用大量内存,或者过多的数据放入了JVM缓存,或性能压力很高消息累积并占用内存,因此在性能测试中,会生成大量的临时对象,并且在GC期间无法进行有效的恢复,甚至根本无法恢复,从而导致内存空间和内存不足溢出。
如果在编码之前估算了内存使用率,则将对内存中的数据进行评估以确保尽快释放有用的信息,并且GC可以回收无用的信息,从而可以在一定程度上避免内存溢出程度值得怀疑。
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/shoujiruanjian/article-371242-1.html
有着不属于这个年龄的成熟
日本海自完全没有战斗机
他有九零后这个群体新锐逗玩不服输的鲜特质