Let's Go!

淘宝测试blog推荐_02

上一篇 / 下一篇  2011-07-29 18:21:03 / 个人分类:性能测试理论

 

1. 性能测试瓶颈分析

在性能测试过程中,瓶颈犹如功能测试的bug,瓶颈的分析犹如bug的定位。性能测试工程师好比医生,看到病象,定位病因。性能瓶颈的定位更像庖丁解牛,层层解剖,最后定位问题之所在。下面分享一个内存泄漏的瓶颈分析。

病象:TPS波动非常大;狂打超时日志;偶尔有500错误。

看到这个现象,其实说明不了什么问题,就象人咳嗽,不一定是感冒,可能是上火,嗓子发炎。但是看到这个现象至少说明系统是有性能问题存在,我们就要进一步进行分析,看看问题到底在哪?用jconsole监控内存,发现内存使用如图1

图1:内存使用情况图

从图1中,我们可以很清晰的看到内存使用不正常,FGC非常频繁,差不多5分钟进行一次,而且内存回收不彻底,每次回收在1G左右徘徊。到这里我们已经可以定位是内存问题,导致了我们看到的TPS波动大,FGC频繁,超时严重等等一系列现象。

那么是谁吃了我的内存???

用简单的jstat命令查看系统GC情况,看到情况如图2所示

图2

在图2的绿色框标注,我们可以很清晰的看到进行一次FGC,内存只回收12%左右,回收很不彻底,而且FGC的时间持续5秒。内存回收不彻底,肯定是有些方法霸占了内存不释放,导致系统频繁FGC来进行回收。

那么谁是真正的凶手呢??

用jmap 命令对内存使用进行分析,发现情况如图3所示

图3

通过FGC前后的内存使用进行比对,发现这三个方法快速占用内存从最少到最多,而且回收不掉,始终霸占着前几位。再通过其他工具分析,看看这三个是不是真正的凶手。

用MAT分析工具进行分析,图4所示

图4

这三个方法各占了内存使用的14%,那么问题就很清晰明朗了。这三个方法就是真正的凶手,调优就从这三个方法入手。

性能瓶颈的分析,犹如庖丁解牛,层层剖析。最终定位问题之所在。

VN:F [1.9.7_1111]

 

转载务必注明出处Taobao QA Team,原文地址:http://qa.taobao.com/?p=8641

 

2. JAVA-OPTS引发的思考

 

我们在性能测试过程中,经常要修改应用的JAVA-OPTS参数。修改这些参数,不单单是修改这些数字,本着知其所以然的态度,我们要知道这些参数背后的意义。

常见的JAVA-OPTS的配置项。

JAVA_OPTS=”-server -Xms1536m -Xmx1536m -XX:NewSize=320m -XX:MaxNewSize=320m -XX:PermSize=96m -XX:MaxPermSize=256m -Xmn500m -XX:MaxTenuringThreshold=5″

-Xms设置堆内存池的最小值

-Xmx:设置堆内存池的最大值

-XX:NewSize设置新对象生产堆内存

-XX:MaxNewSize设置最大新对象生产堆内存

-XX:PermSize设置Perm区的大小

XX:MaxPermSize设置Perm区的最大值

-Xmn设置Young区的大小

-XX:MaxTenuringThreshold设置垃圾最大年龄。

更加详细的说明,请参考图1

图1

看到这里,或许你会迷糊什么是堆内存,什么Perm区?这样就引出了第二个思考:JAVA内存模型。

JAVA内存模型如图2所示。

图2

JAVA的内存模型可以分为3个代:Young、Tenured、Perm。有的版本又叫:New、Old、Perm。中文叫:年轻代、终生代、永久代。或许中文还有其他的叫法,但是表示的意思是一样的。

Young和Tenured共同组成了堆内存(heap)。

Young(年轻代)还可以分为Eden区和两个Survivor区(from和to,这两个Survivor区大小严格一至)。新的对象实例总是首先放在Eden区,Survivor区作为Eden区和 Tenured(终生代)的缓冲,可以向 Tenure(终生代)转移活动的对象实例。如图3所示。

图3

在明白了JAVA内存模型后,就引出了第三个思考:Jconsole在性能测试中的使用。Jconsole是我们在性能测试中常用的工具,打开Jconsole后,可以看到很多监控图表。当你明白JAVA内存模型后,也就很容易明白Jconsole的监控图表了。如图4所示。

图4

在Jconsole图中,我们可以很明显看到内存可以分为堆内存和非堆在堆内存。在堆的上面有三个柱子,分别代别的是:Eden、Survivor、Old。Old区就是Tenured区。

在了解上述知识后,我们在使用Jconsole来监控应用的内存时,会更加清晰。

VN:F [1.9.7_1111]

 

转载务必注明出处Taobao QA Team,原文地址:http://qa.taobao.com/?p=6621

 

 

3. 谈一谈JVM内存JAVA_OPTS参数

最近几个月,做的性能测试项目中,发现了一些内存方面的问题,其中有涉及到对JBOSS里的JAVA_OPTS配置,例如一下所示;
JAVA_OPTS=”-server -Xms1536m -Xmx1536m -XX:NewSize=320m -XX:MaxNewSize=320m -XX:PermSize=96m -XX:MaxPermSize=256m -Xmn500m -XX:MaxTenuringThreshold=5″
JAVA_OPTS并不是已成不变的,不同的应用、软硬件环境下,要想充分发挥应用的性能,这些参数里边的设置可是非常有技巧和具有经验积累的。
经过查找资料,先看下JAVA_OPTS参数表示的意义。

-server:一定要作为第一个参数,在多个CPU时性能佳
-Xms:初始Heap大小,使用的最小内存,cpu性能高时此值应设的大一些
-Xmx:java heap最大值,使用的最大内存
上面两个值是分配JVM的最小和最大内存,取决于硬件物理内存的大小,建议均设为物理内存的一半。-XX:PermSize:设定内存的永久保存区域
-XX:MaxPermSize:设定最大内存的永久保存区域
-XX:MaxNewSize:
-Xss 15120 这使得JBoss每增加一个线程(thread)就会立即消耗15M内存,而最佳值应该是128K,默认值好像是512k.
+XX:AggressiveHeap 会使得 Xms没有意义。这个参数让jvm忽略Xmx参数,疯狂地吃完一个G物理内存,再吃尽一个G的swap。
-Xss:每个线程的Stack大小
-verbose:gc 现实垃圾收集信息
-Xloggc:gc.log 指定垃圾收集日志文件
-Xmn:young generation的heap大小,一般设置为Xmx的3、4分之一
-XX:+UseParNewGC :缩短minor收集的时间
-XX:+UseConcMarkSweepGC :缩短major收集的时间
提示:此选项在Heap Size 比较大而且Major收集时间较长的情况下使用更合适。

稳定的开发架构环境下,建议出一份有实践、经验论证的JAVA_OPTS配置,能够非常切合实际的服务于当前开发、测试的软件流程。

VN:F [1.9.7_1111]
 

谈一谈JVM内存JAVA_OPTS参数,9.0out of10based on5ratings转载务必注明出处Taobao QA Team,原文地址:http://qa.taobao.com/?p=3395

 

4. JAVA内存泄露

 

最近在写性能宝典,正好内存泄露是其中非常重要的主题,记录下来,欢迎大家参与讨论,给出建议,谢谢!

内存溢出就是你要求分配的内存超出了系统能给你的,系统不能满足需求,于是产生溢出。
JAVA中的内存溢出的导致原因很多,最主要的可能有以下几种:
A. 由于JVM堆内存设置过小,可以通过-Xms -Xmm设置。
B. JVM堆内存是足够的,但只是没有连续的内存空间导致,比如申请连续内存空间的数组:String[] array = new String[10000]。
C.由于导入较多的依赖jar包以及项目本身引用的class太多。
D. 测试过程中生成太多的对象。
E. 缓存池载入太多的等待队列。
F. 还有可能是不断的内存泄露导致最后内存不足溢出

内存泄漏的慨念

  1.c/c++是程序员自己治理内存,Java内存是由GC自动回收的。

  我虽然不是很熟悉C++,不过这个应该没有犯常识性错误吧。

  2.什么是内存泄露?

  内存泄露是指系统中存在无法回收的内存,有时候会造成内存不足或系统崩溃。

  在C/C++中分配了内存不释放的情况就是内存泄露。

  3.Java存在内存泄露

  我们必须先承认这个,才可以接着讨论。虽然Java存在内存泄露,但是基本上不用很关心它,非凡是那些对代码本身就不讲究的就更不要去关心这个了。

  Java中的内存泄露当然是指:存在无用但是垃圾回收器无法回收的对象。而且即使有内存泄露问题存在,也不一定会表现出来。

  4.Java中参数都是传值的。

  对于基本类型,大家基本上没有异议,但是对于引用类型我们也不能有异议。

  Java内存泄露情况

  JVM回收算法是很复杂的,我也不知道他们怎么实现的,但是我只知道他们要实现的就是:对于没有被引用的对象是可以回收的。所以你要造成内存泄露就要做到:

  持有对无用对象的引用!

  不要以为这个很轻易做到,既然无用,你怎么还会持有它的引用? 既然你还持有它,它怎么会是无用的呢?
以下以堆栈更经典这个经典的例子来剖析。
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[] ldElements = elements;
  elements = new Object[2 * elements.length+1];
  System.arraycopy(oldElements,0, elements, 0, size);
 }
}
}
  上面的原理应该很简单,假如堆栈加了10个元素,然后全部弹出来,虽然堆栈是空的,没有我们要的东西,但是这是个对象是无法回收的,这个才符合了内存泄露的两个条件:无用,无法回收。

  但是就是存在这样的东西也不一定会导致什么样的后果,假如这个堆栈用的比较少,也就浪费了几个K内存而已,反正我们的内存都上G了,哪里会有什么影响,再说这个东西很快就会被回收的,有什么关系。下面看两个例子。

  例子1

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

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循环也不会有
 //什么太大的影响吧,你会这么做吗?
}

VN:F [1.9.7_1111]

转载务必注明出处Taobao QA Team,原文地址:http://qa.taobao.com/?p=3625

 

5. jmap -dump:live为啥会触发Full GC

昨天组里的新人小朋友问是不是每次执行jmap -dump:live都会触发一次Full GC,因为当时他在做性能测试时某应用已经好几个小时没有一次FGC了,结果他执行了下dump就增加了次FGC。

我当时模糊回答应该会,以前看过哪篇文章好像提过^-^,不过本着严谨不误导新人小朋友的原则,还是找时间抽空验证实践了把:

测试环境:linux , sun jdk 1.6.07 , 32位

测试结果: jmap -dump:live 以及 jmap -histo:live都会触发Full GC,即使加上JVM参数-XX:+DisableExplicitGC也不影响结果

那么为什么呢? 其实大概猜也能知道,live选项的,如果FGC后,看到的活的对象比没有FGC的自然更精确。

我们来通过源码验证学习一下:

入口自然是 $j2se/src/share/classes/sun/tools/JMap.java

关键点是

VirtualMachine vm = attach(pid);
InputStream in = ((HotSpotVirtualMachine)vm).
heapHisto(live ? LIVE_OBJECTS_OPTION : ALL_OBJECTS_OPTION);

以及

InputStream in = ((HotSpotVirtualMachine)vm).
dumpHeap((Object)filename,
(live ? LIVE_OBJECTS_OPTION : ALL_OBJECTS_OPTION));

于是找到这里

HotSpotVirtualMachine.java:

public InputStream dumpHeap(Object … args) throws IOException {
return executeCommand(“dumpheap”, args);

}

public InputStream heapHisto(Object … args) throws IOException {
return executeCommand(“inspectheap”, args);
}

接着看到

LinuxVirtualMachine.java:

先是创建UNIX socket,然后连到target VM,把dumpheap或inspectheap命令通过socket发过去。

那么具体的inspectheap在sun hotspot核心代码里是如何处理的呢?

看这里 $hotspot/src/share/vm/services/attachListener.cpp

heap_inspection函数有如下关键代码:

VM_GC_HeapInspection heapop(out, live_objects_only /* request full gc */, true /* need_prologue */);

同样dumpheap在hotspot里也是这个文件里处理的:

jint dump_heap(AttachOperation* op, outputStream* out) {

// Request a full GC before heap dump if live_objects_only = true
// This helps reduces the amount of unreachable objects in the dump
// and makes it easier to browse.
HeapDumper dumper(live_objects_only /* request GC */);
int res = dumper.dump(op->arg(0));

这下我们就可以明白了,与实验结果也对上号了

参考资料:http://forums.java.net/jive/message.jspa?messageID=115907

VN:F [1.9.7_1111]

 

转载务必注明出处Taobao QA Team,原文地址:http://qa.taobao.com/?p=10010

 

 


TAG:

测试天堂 引用 删除 ljonathan   /   2011-08-08 16:42:51
loadrunner常见问题整理
http://softtest.chinaitlab.com/LoadRunner/791575.html
测试天堂 引用 删除 ljonathan   /   2011-08-08 10:58:00
深入理解Loadrunner中的Browser Emulation
http://www.51testing.com/html/17/n-98017.html
测试天堂 引用 删除 ljonathan   /   2011-07-29 18:29:28
jprofile
http://www.blogjava.net/fine/archive/2007/05/07/115671.html
测试天堂 引用 删除 ljonathan   /   2011-07-29 18:28:28
http://blog.sina.com.cn/s/blog_4adf62ab0100smcb.html
 

评分:0

我来说两句

Open Toolbar