Android性能测试工具 Emmagee

Emmagee是监控指定被测应用在使用过程中占用机器的CPU、内存、流量资源的性能测试小工具。

支持SDK:Android2.2以及以上版本

Emmagee功能介绍

1、检测当前时间被测应用占用的CPU使用率以及总体CPU使用量

2、检测当前时间被测应用占用的内存量,以及占用的总体内存百分比,剩余内存量

3、检测应用从启动开始到当前时间消耗的流量数

4、测试数据写入到CSV文件中,同时存储在手机中

5、可以选择开启浮窗功能,浮窗中实时显示被测应用占用性能数据信息

6、在浮窗中可以快速启动或者关闭手机的wifi网络

Emmagee如何使用

1、安装Emmagee应用

apk下载地址:http://code.google.com/p/emmagee/downloads/list

2、启动Emmagee,列表中会默认加载手机安装的所有应用

3、选择你需要测试的应用,点击“开始测试”,被测应用会被启动

224554_FHBY_1041545

4、开始你的功能测试吧,测试过程中会自动记录相关性能参数

5、测试完成后回到Emmagee界面,点击“结束测试”,测试结果会保存在手机指定目录的CSV文件中

生成的CSV文件内容见图:
224609_54so_1041545

6、使用Excel打开CSV文件,使用自带的统计图标功能生成统计图:

224618_Ptgl_1041545

Android内存泄漏分析及调试

 首先了解一下dalvik的Garbage Collection:

20131025114957718

 如上图所示,GC会选择一些它了解还存活的对象作为内存遍历的根节点(GC Roots),比方说thread stack中的变量,JNI中的全局变量,zygote中的对象(class loader加载)等,然后开始对heap进行遍历。到最后,部分没有直接或者间接引用到GC Roots的就是需要回收的垃圾,会被GC回收掉。如下图蓝色部分。

20131025115331781

Java内存泄漏指的是进程中某些对象(垃圾对象)已经没有使用价值了,但是它们却可以直接或间接地引用到gc roots导致无法被GC回收。无用的对象占据着内存空间,使得实际可使用内存变小,形象地说法就是内存泄漏了。下面分析一些可能导致内存泄漏的情景。

常见的内存泄漏

 1、非静态内部类的静态实例容易造成内存泄漏

上面的代码中的sInstance实例类型为静态实例,在第一个MainActivity act1实例创建时,sInstance会获得并一直持有act1的引用。当MainAcitivity销毁后重建,因为sInstance持有act1的引用,所以act1是无法被GC回收的,进程中会存在2个MainActivity实例(act1和重建后的MainActivity实例),这个act1对象就是一个无用的但一直占用内存的对象,即无法回收的垃圾对象。所以,对于lauchMode不是singleInstance的Activity, 应该避免在activity里面实例化其非静态内部类的静态实例。

2、activity使用静态成员

由于用静态成员sBackground 缓存了drawable对象,所以activity加载速度会加快,但是这样做是错误的。因为在android 2.3系统上,它会导致activity销毁后无法被系统回收。

label .setBackgroundDrawable函数调用会将label赋值给sBackground的成员变量mCallback。

上面代码意味着:sBackground(GC Root)会持有TextView对象,而TextView持有Activity对象。所以导致Activity对象无法被系统回收。

下面看看android4.0为了避免上述问题所做的改进。

先看看android 2.3的Drawable.Java对setCallback的实现:

看看android 4.0的Drawable.Java对setCallback的实现:

在android 2.3中要避免内存泄漏也是可以做到的, 在activity的onDestroy时调用

以上2个例子的内存泄漏都是因为Activity的引用的生命周期超越了activity对象的生命周期。也就是常说的Context泄漏,因为activity就是context。

想要避免context相关的内存泄漏,需要注意以下几点:

  • 不要对activity的context长期引用(一个activity的引用的生存周期应该和activity的生命周期相同)
  • 如果可以的话,尽量使用关于application的context来替代和activity相关的context
  • 如果一个acitivity的非静态内部类的生命周期不受控制,那么避免使用它;正确的方法是使用一个静态的内部类,并且对它的外部类有一WeakReference,就像在ViewRootImpl中内部类W所做的那样。

3、使用handler时的内存问题

我们知道,Handler通过发送Message与主线程交互,Message发出之后是存储在MessageQueue中的,有些Message也不是马上就被处理的。在Message中存在一个 target,是Handler的一个引用,如果Message在Queue中存在的时间越长,就会导致Handler无法被回收。如果Handler是非静态的,则会导致Activity或者Service不会被回收。 所以正确处理Handler等之类的内部类,应该将自己的Handler定义为静态内部类。

HandlerThread的使用也需要注意:

当我们在activity里面创建了一个HandlerThread,代码如下:

这个代码存在泄漏问题,因为HandlerThread的run方法是一个死循环,它不会自己结束,线程的生命周期超过了activity生命周期,当横竖屏切换,HandlerThread线程的数量会随着activity重建次数的增加而增加。

应该在onDestroy时将线程停止掉:

另外,对于不是HandlerThread的线程,也应该确保activity消耗后,线程已经终止,可以这样做:在onDestroy时调用

4、注册某个对象后未反注册

注册广播接收器、注册观察者等等,比如:

假设我们希望在锁屏界面(LockScreen)中,监听系统中的电话服务以获取一些信息(如信号强度等),则可以在LockScreen中定义一个PhoneStateListener的对象,同时将它注册到TelephonyManager服务中。对于LockScreen对象,当需要显示锁屏界面的时候就会创建一个LockScreen对象,而当锁屏界面消失的时候LockScreen对象就会被释放掉。

但是如果在释放LockScreen对象的时候忘记取消我们之前注册的PhoneStateListener对象,则会导致LockScreen无法被GC回收。如果不断的使锁屏界面显示和消失,则最终会由于大量的LockScreen对象没有办法被回收而引起OutOfMemory,使得system_process进程挂掉。

虽然有些系统程序,它本身好像是可以自动取消注册的(当然不及时),但是我们还是应该在我们的程序中明确的取消注册,程序结束时应该把所有的注册都取消掉。

5、集合中对象没清理造成的内存泄露

我们通常把一些对象的引用加入到了集合中,当我们不需要该对象时,如果没有把它的引用从集合中清理掉,这样这个集合就会越来越大。如果这个集合是static的话,那情况就更严重了。

比如某公司的ROM的锁屏曾经就存在内存泄漏问题:

这个泄漏是因为LockScreen每次显示时会注册几个callback,它们保存在KeyguardUpdateMonitor的ArrayList<InfoCallback>、ArrayList<SimStateCallback>等ArrayList实例中。但是在LockScreen解锁后,这些callback没有被remove掉,导致ArrayList不断增大, callback对象不断增多。这些callback对象的size并不大,heap增长比较缓慢,需要长时间地使用手机才能出现OOM,由于锁屏是驻留在system_server进程里,所以导致结果是手机重启。

6、资源对象没关闭造成的内存泄露

资源性对象比如(Cursor,File文件等)往往都用了一些缓冲,我们在不使用的时候,应该及时关闭它们,以便它们的缓冲及时回收内存。它们的缓冲不仅存在于Java虚拟机内,还存在于Java虚拟机外。如果我们仅仅是把它的引用设置为null,而不关闭它们,往往会造成内存泄露。因为有些资源性对象,比如SQLiteCursor(在析构函数finalize(),如果我们没有关闭它,它自己会调close()关闭),如果我们没有关闭它,系统在回收它时也会关闭它,但是这样的效率太低了。因此对于资源性对象在不使用的时候,应该立即调用它的close()函数,将其关闭掉,然后再置为null.在我们的程序退出时一定要确保我们的资源性对象已经关闭。

程序中经常会进行查询数据库的操作,但是经常会有使用完毕Cursor后没有关闭的情况。如果我们的查询结果集比较小,对内存的消耗不容易被发现,只有在长时间大量操作的情况下才会复现内存问题,这样就会给以后的测试和问题排查带来困难和风险。

7、一些不良代码成内存压力

有些代码并不造成内存泄露,但是它们或是对没使用的内存没进行有效及时的释放,或是没有有效的利用已有的对象而是频繁的申请新内存,对内存的回收和分配造成很大影响的,容易迫使虚拟机不得不给该应用进程分配更多的内存,增加vm的负担,造成不必要的内存开支。

7.1 Bitmap使用不当

  • 及时的销毁。

虽然,系统能够确认Bitmap分配的内存最终会被销毁,但是由于它占用的内存过多,所以很可能会超过Java堆的限制。因此,在用完Bitmap时,要及时的recycle掉。recycle并不能确定立即就会将Bitmap释放掉,但是会给虚拟机一个暗示:“该图片可以释放了”。

  • 设置一定的采样率。

有时候,我们要显示的区域很小,没有必要将整个图片都加载出来,而只需要记载一个缩小过的图片,这时候可以设置一定的采样率,那么就可以大大减小占用的内存。如下面的代码:

  • 巧妙的运用软引用(SoftRefrence)

有些时候,我们使用Bitmap后没有保留对它的引用,因此就无法调用Recycle函数。这时候巧妙的运用软引用,可以使Bitmap在内存快不足时得到有效的释放。如下:

7.2 构造Adapter时,没有使用缓存的 convertView

以构造ListView的BaseAdapter为例,在BaseAdapter中提共了方法:

来向ListView提供每一个item所需要的view对象。初始时ListView会从BaseAdapter中根据当前的屏幕布局实例化一定数量的view对象,同时ListView会将这些view对象缓存起来。当向上滚动ListView时,原先位于最上面的list item的view对象会被回收,然后被用来构造新出现的最下面的list item。这个构造过程就是由getView()方法完成的,getView()的第二个形参 View convertView就是被缓存起来的list item的view对象(初始化时缓存中没有view对象则convertView是null)。

由此可以看出,如果我们不去使用convertView,而是每次都在getView()中重新实例化一个View对象的话,即浪费时间,也造成内存垃圾,给垃圾回收增加压力,如果垃圾回收来不及的话,虚拟机将不得不给该应用进程分配更多的内存,造成不必要的内存开支。ListView回收list item的view对象的过程可以查看:

android.widget.AbsListView.Java--> void addScrapView(View scrap) 方法。

修正示例代码:

 

7.3  不要在经常调用的方法中创建对象,尤其是忌讳在循环中创建对象。可以适当的使用 hashtable , vector 创建一组对象容器,然后从容器中去取那些对象,而不用每次 new 之后又丢弃。

关于内存泄漏的调试

下面的介绍是很早之前的操作方式,目前推荐的方式是集成 LeakCanary 在线分析内存泄露,比以前的要方便很多。

(1).内存监测工具 DDMS --> Heap
无论怎么小心,想完全避免bad code是不可能的,此时就需要一些工具来帮助我们检查代码中是否存在会造成内存泄漏的地方。Android tools中的DDMS就带有一个很不错的内存监测工具Heap(这里我使用eclipse的ADT插件,并以真机为例,在模拟器中的情况类似)。用 Heap监测应用进程使用内存情况的步骤如下:
1. 启动eclipse后,切换到DDMS透视图,并确认Devices视图、Heap视图都是打开的;
2. 将手机通过USB链接至电脑,链接时需要确认手机是处于“USB调试”模式,而不是作为“MassStorage”;
3. 链接成功后,在DDMS的Devices视图中将会显示手机设备的序列号,以及设备中正在运行的部分进程信息;
4. 点击选中想要监测的进程,比如system_process进程;
5. 点击选中Devices视图界面中最上方一排图标中的“Update Heap”图标;
6. 点击Heap视图中的“Cause GC”按钮;
7. 此时在Heap视图中就会看到当前选中的进程的内存使用量的详细情况。
说明:
a) 点击“Cause GC”按钮相当于向虚拟机请求了一次gc操作;
b) 当内存使用信息第一次显示以后,无须再不断的点击“CauseGC”,Heap视图界面会定时刷新,在对应用的不断的操作过程中就可以看到内存使用的变化;
c) 内存使用信息的各项参数根据名称即可知道其意思,在此不再赘述。
如何才能知道我们的程序是否有内存泄漏的可能性呢。这里需要注意一个值:Heap视图中部有一个Type叫做dataobject,即数据对象,也就是我们的程序中大量存在的类类型的对象。在data object一行中有一列是“Total Size”,其值就是当前进程中所有Java数据对象的内存总量,一般情况下,这个值的大小决定了是否会有内存泄漏。可以这样判断:
a) 不断的操作当前应用,同时注意观察data object的Total Size值;
b) 正常情况下Total Size值都会稳定在一个有限的范围内,也就是说由于程序中的的代码良好,没有造成对象不被垃圾回收的情况,所以说虽然我们不断的操作会不断的生成很多对象,而在虚拟机不断的进行GC的过程中,这些对象都被回收了,内存占用量会会落到一个稳定的水平;
c) 反之如果代码中存在没有释放对象引用的情况,则dataobject的Total Size值在每次GC后不会有明显的回落,随着操作次数的增多Total Size的值会越来越大,
直到到达一个上限后导致进程OOM被kill掉。

(2).内存分析工具 MAT(Memory Analyzer Tool)

并不是所有的内存泄漏都可以用观察heap size的方法检测出来,因为有的程序只是泄漏了几个对象,而且泄漏的对象个数不会随着程序的运行而增加,这种内存泄漏不会直接导致OOM,但是无用对象无法回收,无疑是对内存的浪费,会影响到程序的性能,我们需要使用MAT工具才能发现这种比较隐蔽的内存泄漏。
使用MAT之前有2个概念是要掌握的:Shallowheap和Retained heap。Shallow heap表示对象本身所占内存大小,一个内存大小100bytes的对象Shallow heap就是100bytes。Retained heap表示通过回收这一个对象总共能回收的内存,比方说一个100bytes的对象还直接或者间接地持有了另外3个100bytes的对象引用,回收这个对象的时候如果另外3个对象没有其他引用也能被回收掉的时候,Retained heap就是400bytes。

MAT使用Dominator Tree这样一种来自图形理论的概念。

20131025120105203

所谓Dominator,就是Flow Graph中从源节点出发到某个节点的的必经节点。那么根据这个概念我们可以从上图左侧的Flow Graph构造出右侧的Dominator Tree。这样一来很容易就看出每个节点的Retained heap了。Shallow heap和Retained heap在MAT中是非常有用的概念,用于内存泄漏的分析。

我们做一个Demo。在工程的MainActivity当中加入如下代码:

上面这段代码,对Java熟悉的同学都应该了解非静态内部类对象默认持有外部类对象引用,而leak作为静态变量在非空判断下只产生了一个对象,因此当旋转屏幕时生成新的Activity的时候旧的Activity的引用依然被持有,如下图:

20131025120204937

通过观察旋转屏幕前后Log中GC的信息也能看出heap的data object分配往上涨了一点,并且在GC执行完heap的分配稳定之后并没有降下来,这就是内存泄漏的迹象。

我们通过MAT来进行分析。先下载MAT,可以作为Eclipse插件下载,也可以作为RCP应用下载,本质上没有区别。DDMS中选中应用对应的进程名,点击Dump HPROF file的按钮,等一小段时间生成HPROF文件,如果是Eclipse插件的话,Eclipse会为这个HPROF自动转化成标准的HPROF并自动打开MAT分析界面。如果是作为RCP应用的话,需要用sdk目录tools中的hprof-conv工具来进行转化

这种方式保存HPROF文件的位置选择更为自主,你也可以修改Eclipse的设置让Eclipse提示保存而不是自动打开,在Preferences -> Android -> DDMS中的HPROFAction由Open in Eclipse改为Save todisk。打开MAT,选择转化好的HPROF文件,可以看到Overview的界面如下图:20131025120259250

中间的饼状图就是根据我们上文所说的Retained heap的概念得到的内存中一些Retained Size最大的对象。点击饼状图能看到这些对象类型,但对内存泄漏的分析还远远不够。再看下方Action中有Dominator Tree和Histogram的选项,这一般来说是最有用的工具。还记得我们上文说过的DominatorTree的概念吗,这就是我们用来跟踪内存泄漏的方式。点开Dominator Tree,会看到以Retained heap排序的一系列对象,如下图:

20131025120353093

Resources类型对象由于一般是系统用于加载资源的,所以Retained heap较大是个比较正常的情况。但我们注意到下面的Bitmap类型对象的Retained heap也很大,很有可能是由于内存泄漏造成的。所以我们右键点击这行,选择Path To GC Roots ->exclude weak references,可以看到下图的情形:20131025120457531

Bitmap最终被leak引用到,这应该是一种不正常的现象,内存泄漏很可能就在这里了。MAT不会告诉哪里是内存泄漏,需要你自行分析,由于这是Demo,是我们特意造成的内存泄漏,因此比较容易就能看出来,真实的应用场景可能需要你仔细的进行分析。

根据我们上文介绍的Dominator的概念,leak对象是该Bitmap对象的Dominator,应该出现在Dominator Tree视图里面,但实际上却没有。这是由于MAT并没有对weak references做区别对待,这也是我们选择exclude weakreferences的原因。如果我们Path To GC Roots ->with all references,我们可以看到下图的情形:

20131025120731703

可以看到还有另外一个对象在引用着这个Bitmap对象,了解weak references的同学应该知道GC是如何处理weak references,因此在内存泄漏分析的时候我们可以把weak references排除掉。

有些同学可能希望根据某种类型的对象个数来分析内存泄漏。我们在Overview视图中选择Actions -> Histogram,可以看到类似下图的情形:

20131025120850406

上图展示了内存中各种类型的对象个数和Shallow heap,我们看到byte[]占用Shallow heap最多,那是因为Honeycomb之后Bitmap Pixel Data的内存分配在Dalvik heap中。右键选中byte[]数组,选择List Objects -> with incomingreferences,可以看到byte[]具体的对象列表:

20131025123513640

20131025123529984

我们发现第二个byte[]的Retained heap较大,内存泄漏的可能性较大,因此右键选中这行,Path To GC Roots -> exclude weak references,同样可以看到上文所提到的情况,我们的Bitmap对象被leak所引用到,这里存在着内存泄漏。

20131025121635500
20131025123529984

在Histogram视图中第一行<Regex>中输入com.example.android.hcgallery,过滤出我们自己应用中的类型,如下图:

20131025121711500

我们发现本应该只有一个MainActivity现在却有两个,显然不正常。右键选择List Objects-> with incoming references,可以看到这两个具体的MainActivity对象。右键选中Retained heap较大的MainActivity,Path To GC Roots -> exclude weak references,再一次可疑对象又指向了leak对象。

201310251219069842013102512191714020131025121925796

引用 http://blog.csdn.net/gemmem/article/details/13017999

Android进程的内存管理分析

首先,回顾一下基础知识,基础知识是理解系统机制的前提和关键:

1、  进程的地址空间

在32位操作系统中,进程的地址空间为0到4GB,

示意图如下:

20130513154042627

这里主要说明一下Stack和Heap:

Stack空间(进栈和出栈)由操作系统控制,其中主要存储函数地址、函数参数、局部变量等等,所以Stack空间不需要很大,一般为几MB大小。

Heap空间的使用由程序员控制,程序员可以使用malloc、new、free、delete等函数调用来操作这片地址空间。Heap为程序完成各种复杂任务提供内存空间,所以空间比较大,一般为几百MB到几GB。正是因为Heap空间由程序员管理,所以容易出现使用不当导致严重问题。

2、进程内存空间和RAM之间的关系

进程的内存空间只是虚拟内存(或者叫作逻辑内存),而程序的运行需要的是实实在在的内存,即物理内存(RAM)。在必要时,操作系统会将程序运行中申请的内存(虚拟内存)映射到RAM,让进程能够使用物理内存。

RAM作为进程运行不可或缺的资源,对系统性能和稳定性有着决定性影响。另外,RAM的一部分被操作系统留作他用,比如显存等等,内存映射和显存等都是由操作系统控制,我们也不必过多地关注它,进程所操作的空间都是虚拟地址空间,无法直接操作RAM。

示意图如下:

20130513154359212

3、  Android中的进程

(1)   native进程:采用C/C++实现,不包含dalvik实例的进程,/system/bin/目录下面的程序文件运行后都是以native进程形式存在的。如下图所示,/system/bin/surfaceflinger、/system/bin/rild、procrank等就是native进程。

(2)   java进程:Android中运行于dalvik虚拟机之上的进程。dalvik虚拟机的宿主进程由fork()系统调用创建,所以每一个java进程都是存在于一个native进程中,因此,java进程的内存分配比native进程复杂,因为进程中存在一个虚拟机实例。如下图,Android系统中的应用程序基本都是java进程,如桌面、电话、联系人、状态栏等等。

20130513155151825

4、  Android中进程的堆内存

进程空间中的heap空间是我们需要重点关注的。heap空间完全由程序员控制,我们使用的malloc、C++ new和java new所申请的空间都是heap空间, C/C++申请的内存空间在native heap中,而java申请的内存空间则在dalvik heap中。

20130513155252901

5、  Android的 java程序为什么容易出现OOM

这个是因为Android系统对dalvik的vm heapsize作了硬性限制,当java进程申请的java空间超过阈值时,就会抛出OOM异常(这个阈值可以是48M、24M、16M等,视机型而定),可以通过adb shell getprop | grep dalvik.vm.heapgrowthlimit查看此值。

也就是说,程序发生OMM并不表示RAM不足,而是因为程序申请的java heap对象超过了dalvik vm heapgrowthlimit。也就是说,在RAM充足的情况下,也可能发生OOM。

这样的设计似乎有些不合理,但是Google为什么这样做呢?这样设计的目的是为了让Android系统能同时让比较多的进程常驻内存,这样程序启动时就不用每次都重新加载到内存,能够给用户更快的响应。迫使每个应用程序使用较小的内存,移动设备非常有限的RAM就能使比较多的app常驻其中。但是有一些大型应用程序是无法忍受vm heapgrowthlimit的限制的,后面会介绍如何让自己的程序跳出vm heapgrowthlimit的限制。

6、  Android如何应对RAM不足

在第5点中提到:java程序发生OMM并不是表示RAM不足,如果RAM真的不足,会发生什么呢?这时Android的memory killer会起作用,当RAM所剩不多时,memory killer会杀死一些优先级比较低的进程来释放物理内存,让高优先级程序得到更多的内存。我们在分析log时,看到的进程被杀的log,如图5,往往就是属于这种情况。

20130513155457553

7、  如何查看RAM使用情况

可以使用

查看RAM使用情况:

这里对其中的一些字段进行解释:

MemTotal:可以使用的RAM总和(小于实际RAM,操作系统预留了一部分)

MemFree:未使用的RAM

Cached:缓存(这个也是app可以申请到的内存)

HightTotal:RAM中地址高于860M的物理内存总和,只能被用户空间的程序使用。

HightFree:RAM中地址高于860M的未使用内存

LowTotal:RAM中内核和用户空间程序都可以使用的内存总和(对于512M的RAM: lowTotal= MemTotal)

LowFree: RAM中内核和用户空间程序未使用的内存(对于512M的RAM: lowFree = MemFree)

8、  如何查看进程的内存信息

(1)、使用

从下图可以看出,com.example.demo作为java进程有2个heap,native heap和dalvik heap,native heap size为159508KB,dalvik heap size为46147KB

20130513155636097

(2)使用

查看进程内存信息

20130513155739454

解释一些字段的意思:

VSS- Virtual Set Size 虚拟耗用内存(包含共享库占用的内存)

RSS- Resident Set Size 实际使用物理内存(包含共享库占用的内存)

PSS- Proportional Set Size 实际使用的物理内存(比例分配共享库占用的内存)

USS- Unique Set Size 进程独自占用的物理内存(不包含共享库占用的内存)

一般来说内存占用大小有如下规律:VSS >= RSS >= PSS >= USS

注意:procrank可以查看native进程和java进程,而dumpsys meminfo只能查看java进程。

9、  应用程序如何绕过dalvikvm heapsize的限制

对于一些大型的应用程序(比如游戏),内存使用会比较多,很容易超超出vm heapsize的限制,这时怎么保证程序不会因为OOM而崩溃呢?

(1)创建子进程

创建一个新的进程,那么我们就可以把一些对象分配到新进程的heap上了,从而达到一个应用程序使用更多的内存的目的,当然,创建子进程会增加系统开销,而且并不是所有应用程序都适合这样做,视需求而定。

创建子进程的方法:使用android:process标签

(2)使用jni在native heap上申请空间(推荐使用)

nativeheap的增长并不受dalvik vm heapsize的限制,从图6可以看出这一点,它的native heap size已经远远超过了dalvik heap size的限制。

只要RAM有剩余空间,程序员可以一直在native heap上申请空间,当然如果 RAM快耗尽,memory killer会杀进程释放RAM。大家使用一些软件时,有时候会闪退,就可能是软件在native层申请了比较多的内存导致的。比如,我就碰到过UC web在浏览内容比较多的网页时闪退,原因就是其native heap增长到比较大的值,占用了大量的RAM,被memory killer杀掉了。

(3)使用显存(操作系统预留RAM的一部分作为显存)

使用OpenGL textures等API,texture memory不受dalvik vm heapsize限制,这个我没有实践过。再比如Android中的GraphicBufferAllocator申请的内存就是显存。

10、Bitmap分配在native heap还是dalvik heap上?

答案是 Android 2.X 版本上,大部分都是在 native heap上面,dalvik heap上保存引用,因此才会在Android 文档上明确推荐手动调用 recycle 函数回收内存。从3.X版本开始,都在dalvik heap上开辟内存,因此也就没有上述的推荐调用了。

摘自 http://blog.csdn.net/gemmem/article/details/8920039

linux top命令的TIME/TIME+值

top命令的TIME/TIME+是指的进程所使用的CPU时间,不是进程启动到现在的时间,因此,如果一个进程使用的cpu很少,那即使这个进程已经存在N长时间,TIME/TIME+也是很小的数值。

此外,如果你的系统有多个CPU,或者是多核CPU的话,那么,进程占用多个cpu的时间是累加的。

top的manual page:http://linux.die.net/man/1/top
看看他的帮助说明:
l: TIME — CPU Time
Total CPU time the task has used since it started. When ‘Cumulative mode’ is On, each process is listed with the cpu time that it and its dead children has used. You toggle ‘Cumulative mode’ with ‘S’, which is a command-line option and an interactive command. See the ‘S’ interactive command for additional information regarding this mode.
m: TIME+ — CPU Time, hundredths
The same as ‘TIME’, but reflecting more granularity through hundredths of a second.

参考 http://1.guotie.sinaapp.com/?p=24

WordPress安装插件提示“无法创建目录”

WordPress安装插件提示“无法创建目录”,这个是由于目录的权限设置导致的,一般情况下可以直接给目录/wp-content目录下面的plugins  themes   uploads upgrade目录增加 777 权限即可,但是这样做会导致服务器配置的不够安全,因此找到个更合适的方案处理。

前提,服务器系统为Ubuntu 12.04 版本,几个目录的默认所有者为 root,使用的FTP为 vsftpd ,服务器为 Apache2 ,这些条件主要限制了默认的用户组名字,如果系统不同,用户组的名字可能需要调整一下。

1.给这几个目录 755 权限,即,只有所有者才有权限写入。

2.逐个授权,vsftpd 的默认用户分组为ftpgroup,用户名自定义的,我们假定为 ftpuser.  Apache2 的用户分组默认都为www-data,其中plugins  themes  upgrade目录主要是ftp安装插件用的,因此写入权限只要给ftpuser 就可以了,其他用户读取权限足够了。而uploads目录是上传图片资源文件的目录,因此只要给www-data 用户写入权限就可以了。

这样子,基本就安全多了。

Android + Eclipse: No grammar constraints (DTD or XML schema) detected for the document.

I don’t like having warnings in the code, and recently I’ve been getting a lot of these, these seem to popup after you accidentally press the Validate button in Eclipse…

Anyway, it is a easy fix, set the Validator to ignore (See screenshot below). Once you have changed the setting, you may have to Validate the files again (right click file -> Validate)dtd
原文 http://ucla.jamesyxu.com/?p=148

Ubuntu 12.04 Apache2安装PHP-FPM增强PHP5-CGI的功能

FPM(FastCGI 进程管理器)用于替换 PHP FastCGI 的大部分附加功能,对于高负载网站是非常有用的。

它的功能包括:

  • 支持平滑停止/启动的高级进程管理功能;
  • 可以工作于不同的 uid/gid/chroot 环境下,并监听不同的端口和使用不同的 php.ini 配置文件(可取代 safe_mode 的设置);
  • stdout 和 stderr 日志记录;
  • 在发生意外情况的时候能够重新启动并缓存被破坏的 opcode;
  • 文件上传优化支持;
  • "慢日志" - 记录脚本(不仅记录文件名,还记录 PHP backtrace 信息,可以使用 ptrace或者类似工具读取和分析远程进程的运行数据)运行所导致的异常缓慢;
  • fastcgi_finish_request() - 特殊功能:用于在请求完成和刷新数据后,继续在后台执行耗时的工作(录入视频转换、统计处理等);
  • 动态/静态子进程产生;
  • 基本 SAPI 运行状态信息(类似Apache的 mod_status);
  • 基于 php.ini 的配置文件。

1.安装PHP-FPM 支持模块

2.重启 Apache2

这时候使用

可以看到,进程中大约出现四五个 php5-fpm 进程。

注意 php5-cgi 进程的最大数量可以在 /etc/apache2/mods-available/fcgid.conf 中配置 DefaultMaxClassProcessCount 来限制。

Ubuntu 12.04下安装配置Worker工作模式的Apache支持PHP

其实Apache本身的并发能力是比较强大的,但是Debian/Ubuntu默认安装的是Prefork模式下的Apache。所以导致很多人后面盲目的去使用lighttpdnginx一类替代软件。但是这类软件有一定的兼容问题,部分情况下可能工作的并不好。那么, 是不是Apache并发就不行了呢?答案当然是否定的。

在进行配置之前,我们首先要知道什么是Prefork模式,什么是Worker模式,什么是Event模式,以及什么是MPM

MPM(Multi-Processing Modules,表示Apache中的多路处理模块)是Apache2引入的一个概念,就是将结构模块化。把核心任务处理作为一个可插拔的模块,即MPM,使其能针对不同的环境进行优化。在这个情况下,就诞生出了处理模式的概念。处理模式现在分为PreforkWorkerEvent三种。

Prefork MPM基于非线程模型,和Apache 1.x版本中的处理方式很相似。Prefork MPM在所有情况下都很安全,对运行非线程安全(non-thread-safe)模式的软件如PHP,它是唯一的安全选择。对于某些应用程序,包括在Apache 1.3上非常流行的程序(如简单静态页面、CGI脚本等),Prefork MPM是最好的选择。另一方面,prefork用单独的子进程来处理不同的请求,进程之间是彼此独立的,这也使其成为最稳定的MPM之一。但是由于每一个请求都会产生一个新的进程,导致系统资源(尤其是内存)消耗的很快,一旦并发量较大的时候,大量的Apache进程会占用巨大的内存空间。

Worker MPM基于线程模式,具有内存消耗低(对繁忙的服务很重要)、扩展性在某些特定应用情况下比Prefork更好等优点。在这个模式下,采用的进程和线程混合的形式处理请求。由于使用线程来处理,所以可以处理相对海量的请求,而系统资源的开销要小于基于进程的Prefork模式。

以上两种稳定的MPM方式在非常繁忙的服务器应用下都有些不足。尽管HTTPKeepalive方式能减少TCP连接数量和网络负载,但是Keepalive需要和服务进程或者线程绑定,这就导致一个繁忙的服务器会耗光所有的线程。Event MPM是解决这个问题的一种新模型,它把服务进程从连接中分离出来。在服务器处理速度很快,同时具有非常高的点击率时,可用的线程数量就是关键的资源限制,此时Event MPM方式是最有效的。一个以Worker MPM方式工作的繁忙服务器能够承受每秒好几万次的访问量(例如在大型新闻服务站点的高峰时),而Event MPM可以用来处理更高负载。值得注意的是,Event MPM在Apache 2.4 之前的版本中不能在安全HTTPHTTPS)访问下工作,Ubuntu 12.04中默认安装的版本是Apache 2.2因此还不能完全使用。

一目了然,三种MPM模式各有各的优缺点。但是如果我们经常遇到访问量一大,服务器资源就吃紧的情况,那么就是Prefork模式瓶颈了。在其他两类MPM中,通用的做法还是使用Worker模式来解决问题。Event MPM由于不支持安全连接(HTTPS)所以导致应用有一定的局限性。

下面我们就以Debian/Ubuntu下将Apache的模式从Prefork设置为Worker为例,来说明一下操作步骤。前面也提到了,由于Worker模式与PHP的执行方式不同,所以如果简单的输入

会导致PHP无法使用。当然了,如果你的网页只有静态页面,不需要使用PHP,那么使用上面这条指令就会搞定一切。这里我们着重讨论下要使用PHP的情况下,应该如何配置ApacheWorker模式。

先查看Apache当前的加载的模块

查看到输出为

可以看到其中的

1. 安装Apache的fcgid模块,使用它来启用PHP。

2. 设置fcgid模块的配置文件,使其能够调用PHP。

可以看到内容为如下

在 AddHandler 后面增加 PHP支持,修改为

注意 DefaultMaxClassProcessCount 10 用来限制php5-cgi 最大的进程并发数,这个可以避免创建了太多的php5-cgi进程,耗尽系统内存,导致OOM Killer 杀死服务的问题,后面的数字根据自己机器的配置情况自行调整即可。

3. 安装php5-cgi

4. 设置Apache的配置文件,使其能够调用fcgid模块来启动PHP。

原内容为

修改为

即在<Directory /var/www/></Directory>之间增加

5. 安装Apache的Worker MPM模块。

需要注意的是,如果之前有安装了php5-gd和php5-mysql模块,Ubuntu在配置Apache的Worker模式的时候可能会将其卸载,可以重新安装一次以防万一:

完成之后继续执行

查看到输出为

可以看到其中的

Eclipse上安装ivy插件

Apache Ivy 是Apache Ant 下的一个子项目。Apache Ivy是一个优秀的管理(记录、跟踪、解析和报告)项目依赖的工具,提供了强大的依赖管理功能,可与Apache Ant紧密集成。项目相关网址:(http://ant.apache.org/ivy/

ivyDe是eclipse的一个插件,用来支持ivy。

要安装ivyDe,可以从eclipse在线安装,地址是http://www.apache.org/dist/ant/ivyde/updatesite

Ubuntu 12.04/14.04/18.04使用squid架设Http正向代理服务器

配置普通HTTP正向代理

安装

配置squid3

搜索此句

修改为

搜索此句(squid默认允许访问本地(localhost)服务,但建议禁止

修改为

启动/重启squid3

注意: squidhttp默认代理端口为3128

配置HTTPS正向代理(单向认证)

由于ubuntu 12.04/14.04/18.04默认安装的squid版本是不支持SSL的,因此只能重新编译支持SSL.

1.安装编译依赖

2.修改默认的编译选项,增加SSL支持
ubuntu 12.04

DEB_CONFIGURE_EXTRA_FLAGS字段中增加--enable-ssl

ubuntu 14.04/18.04

对于Ubuntu 14.04来说,除了要在DEB_CONFIGURE_EXTRA_FLAGS字段中增加--enable-ssl外,由于squid3-3.3.8默认去掉了对于NCSA的支持,而我们下面认证密码的配置是基于NCSA算法的,因此需要在DEB_CONFIGURE_EXTRA_FLAGS--enable-auth-basic字段中增加对于NCSA的支持,否则无法使用NCSA算法进行账号认证,修改后的样子如下:

注意,这样修改后squid3-3.3.8版本编译出来的在/usr/lib/squid3/下的ncsa_auth被重命名成了basic_ncsa_auth,因此配置文件中制定认证应用的时候要修改一下。

对于Ubuntu 18.04来说,这个参数不需要调整,默认支持NCSA

注意,如果需要禁用ipv6的支持,则增加--disable-ipv6选项,这个目前是唯一的可以彻底禁用ipv6代理访问的方式。这个需求的原因是由于配置问题,某些网站的ipv6地址存在,但是无法正常访问

3.配置编译
ubuntu 12.04

ubuntu 14.04

ubuntu 18.04

4.安装(编译后的文件存在在上层的squid_src目录下面)
ubuntu 12.04

ubuntu 14.04

ubuntu 18.04

5.验证安装后的版本是否支持SSL

如果看到enable-ssl输出,则说明编译成功。

6.生成自签名证书

7.移动服务器证书到squid3配置目录

8.配置squit3

搜索https_port,如果搜索到,则在搜索到的位置增加,搜索不到则在文件的最后增加

9.验证配置文件是否配置正确

10.启动/重启squid

11.安装stunnel,配置安装目录下的stunnel.conf,把原来的内容都删掉,粘贴下面的内容

12.设置浏览器代理服务器地址

7442F5AA-EF60-49C8-B7BB-97D750C72231

13.重新启动stunnel

注意,如果只支持Https协议,可以注释掉原来的 

配置Squid3用户名密码认证

1. 安装htpasswd(如果装了apache就省掉这一步)
(这个是用来产生密码的)

也可以

2.生成密码文件

会让你输入user1的密码
3.改一下权限,确保squid能够读出密码文件

4.修改squid.conf,增加或修改成下面的样子

对于ubuntu 12.04版本上编译出来的的Squid3-3.1.19,执行如下配置:

对于ubuntu 14.04版本上编译出来的的Squid3-3.3.8,由于验证程序的名字被修改成了basic_ncsa_auth,因此执行如下配置:

简单的修改可以在VIM中查询 "auth_param basic program /usr/lib/squid/ncsa_auth" 找到后修改一下即可.
然后搜索 "http_access allow all" 找到后替换为 "http_access allow ncsa_users"
在"http_access allow all" 上面就是acl控制部分,直接增加"acl ncsa_users proxy_auth REQUIRED"
如果配置文件是从Squid3-3.1.19升级到Squid3-3.3.8版本的,那么根据如下两个链接的描述:
http://wiki.squid-cache.org/SquidFaq/ConfiguringSquid
https://forums.gentoo.org/viewtopic-t-952948-start-0.html

这两个链接上的信息,从Squid3-3.2版本开始,原来配置文件中的acl控制信息已经不需要再从配置文件中读取了,这部分的功能已经集成进入应用内部了。
因此需要注释掉下面这几句话,否则无法正常启动Squid3.

5.重启squid3

限制单个用户的下载速度

squid3在默认编译的时候,已经包含了--enable-delay-pools选项,因此已经默认支持限速了,我们只需要设置限速配置就可以了。

在文件最后增加如下配置:

参数的具体解释请在squid.conf中搜索delay-pools查看。

重启squid3

参考链接