前几天博客搬家时,本来搬家是因为buyvm的vps不稳定,老是offline,直到昨天才发现原来是博客被人黑了。
被人挂上了后门用于发送垃圾邮件,导致CPU负载猛增服务掉线。找到问题就好办了,搜索所有的代码,把有嫌疑的文件全部干掉,删除不用的主题和插件。分析访问日志查到POST方式请求的文件删除。把不该有写入权限的文件夹去除写入权限,并修改ssh密码等。
去年也发现一个事,域名被人劫持了。现到在百度上都有一些乱七八糟的信息。
看来安全问题还是得注意了。
前几天博客搬家时,本来搬家是因为buyvm的vps不稳定,老是offline,直到昨天才发现原来是博客被人黑了。
被人挂上了后门用于发送垃圾邮件,导致CPU负载猛增服务掉线。找到问题就好办了,搜索所有的代码,把有嫌疑的文件全部干掉,删除不用的主题和插件。分析访问日志查到POST方式请求的文件删除。把不该有写入权限的文件夹去除写入权限,并修改ssh密码等。
去年也发现一个事,域名被人劫持了。现到在百度上都有一些乱七八糟的信息。
看来安全问题还是得注意了。
之前博客放置在buyvm的vps上,但最近几个月非常不稳定,老是offline.
所以今天把博客搬到了在hostshare上购买的16.5元的vps上,速度还不错,先用用。不行只能再搬家了
HostShare是一个主机分享组织,成立于2008年,目前主力经营低价VPS;数据中心位于洛杉矶mc,2014新年全场促销码:2014OFF
全场超低折扣,以下为2套特价方案:
至今仍保持hostloc最低价格记录,性价比一流;由loc多位资深会员投资,目前由单手摘月妹子负责运营。本次优惠适用于openvz和xen全系列架构。
背景:
大学里学java,老师口口声声,言之凿凿,告诫我们,Java千万别用异常控制业务流程,只有系统级别的问题,才能使用异常;
(当时,我们都不懂为什么不能用异常,只知道老师这么说,我们就这么做,考试才不会错 🙂 )
公司里,有两派.异常拥护者说,使用业务异常,代码逻辑更清晰,更OOP;反之者说,使用异常,性能非常糟糕;
(当然,我是拥护者)
论坛上,争论得更多,仁者见仁智者见智,口水很多;
(我不发表意见,其实怎么用,真的都可以)
那么,为什么反对异常呢?貌似大多数人的原因都只有一个:性能差!
使用异常性能真的差吗? 是的!
是什么原因,导致性能问题呢? 那么请看下文…
根本原因在于:
异常基类Throwable.java的public synchronized native Throwable fillInStackTrace()方法
方法介绍:
Fills in the execution stack trace. This method records within this Throwable object information about the current state of the stack frames for the current thread.
性能开销在于:
1. 是一个synchronized方法(主因)
2. 需要填充线程运行堆栈信息
但是对于业务异常来说,它只代表业务分支;压根儿不需要stack信息的.如果去掉这个过程,是不是有性能提升呢?
于是做了一个简单的测试对比,对比主体:
1。 创建普通Java对象 (CustomObject extends HashMap)
2。 创建普通Java异常对象 (CustomException extends Exception)
3。 创建改进的Java业务异常对象 (CustomException extends Exception,覆写fillInStackTrace方法,并且去掉同步)
测试结果:
(运行环境:xen虚拟机,5.5G内存,8核;jdk1.6.0_18)
(10个线程,创建10000000个对象所需时间)
普通Java对象 45284 MS
普通java异常 205482 MS
改进的Java业务异常 16731 MS
测试代码如下:
所以,如果我们业务异常的基类,一旦覆写fillInStackTrace,并且去掉同步,那么异常性能有大幅度提升(因为业务异常本身也不需要堆栈信息)
如果说,创建异常的性能开销大家已经有些感觉了,那么TryCatch是否也存在性能开销呢?
接下来,做了一次try…catch 和 if…esle的性能比较
测试结果(运行环境和上面一样):
20个线程,100000000,所消耗的时间:
try…catch: 101412MS
if…else: 100749MS
备注:
在我自己的开发机器上(xp和ubuntu下,单核),try…catch耗时是if…else的2倍(在同一数量级)
具体原因还未知,之后会使用专业的性能测试工具进行分析
测试代码如下:
结论:
1。Exception的性能是差,原因在于ThrowablefillInStackTrace()方法
2. 可以通过改写业务异常基类的方法,提升性能
3。try…catch和if…else的性能开销在同一数量级
4。至于是否使用异常进行业务逻辑的控制,主要看代码风格.(我个人挺喜欢业务异常的)
备注:
以上测试比较简单,写得也比较急.此文也写得比较急(工作时间偷偷写).如果分析不到位的地方,请指出.
大部分人使用HttpClient都是使用类似上面的事例代码,包括Apache官方的例子也是如此。最近我在使用HttpClient是发现一次循环发送大量请求到服务器会导致APACHE服务器的链接被占满,后续的请求便排队等待。
我服务器端APACHE的配置
因此这样的配置就会导致每个链接至少要过180S才会被释放,这样在大量请求访问时就必然会造成链接被占满,请求等待的情况。
在通过DEBUH后发现HttpClient在method.releaseConnection()后并没有把链接关闭,这个方法只是将链接返回给connection manager。如果使用HttpClient client = new HttpClient()实例化一个HttpClient connection manager默认实现是使用SimpleHttpConnectionManager。SimpleHttpConnectionManager有个构造函数如下
看方法注释我们就可以看到如果alwaysClose设为true在链接释放之后connection manager 就会关闭链。在我们HttpClient client = new HttpClient()这样实例化一个client时connection manager是这样被实例化的
因此alwaysClose默认是false,connection是不会被主动关闭的,因此我们就有了一个客户端关闭链接的方法。
方法一:
把事例代码中的第一行实例化代码改为如下即可,在method.releaseConnection();之后connection manager会关闭connection 。
方法二:
实例化代码使用:HttpClient client = new HttpClient();
在method.releaseConnection();之后加上
shutdown源代码很简单,看了一目了然
方法三:
实例化代码使用:HttpClient client = new HttpClient();
在method.releaseConnection();之后加上
client.getHttpConnectionManager().closeIdleConnections(0);此方法源码代码如下:
将idleTimeout设为0可以确保链接被关闭。
以上这三种方法都是有客户端主动关闭TCP链接的方法。下面再介绍由服务器端自动关闭链接的方法。
方法四:
代码实现很简单,所有代码就和最上面的事例代码一样。只需要在HttpMethod method = new GetMethod(“http://www.apache.org”);加上一行HTTP头的设置即可
看一下HTTP协议中关于这个属性的定义:
HTTP/1.1 defines the “close” connection option for the sender to signal that the connection will be closed after completion of the response. For example,
Connection: close
现在再说一下客户端关闭链接和服务器端关闭链接的区别。如果采用客户端关闭链接的方法,在客户端的机器上使用netstat –an命令会看到很多TIME_WAIT的TCP链接。如果服务器端主动关闭链接这中情况就出现在服务器端。
参考WIKI上的说明http://wiki.apache.org/HttpComponents/FrequentlyAskedConnectionManagementQuestions
The TIME_WAIT state is a protection mechanism in TCP. The side that closes a socket connection orderly will keep the connection in state TIME_WAIT for some time, typically between 1 and 4 minutes.
TIME_WAIT的状态会出现在主动关闭链接的这一端。TCP协议中TIME_WAIT状态主要是为了保证数据的完整传输。具体可以参考此文档:
http://www.softlab.ntua.gr/facilities/documentation/unix/unix-socket-faq/unix-socket-faq-2.html#ss2.7
另外强调一下使用上面这些方法关闭链接是在我们的应用中明确知道不需要重用链接时可以主动关闭链接来释放资源。如果你的应用是需要重用链接的话就没必要这么做,使用原有的链接还可以提供性能。
子线程更新UI会发生android.view.ViewRoot$CalledFromWrongThreadException异常的解决方法
子线程更新UI
显然假如你的程序需要执行耗时的操作的话,假如像上例一样由主线程来负责执行该操作是错误的。所以我们需要在onClick方法中创建一个新的子线程来负责调用GOOGLE API来获得天气数据。刚接触Android的开发者最轻易想到的方式就是如下:
public void onClick(View v) { //创建一个子线程执行耗时的从网络上获得天气信息的操作 new Thread() { @Override public void run() { //获得用户输入的城市名称 String city = editText.getText().toString(); //调用Google 天气API查询指定城市的当日天气情况 String weather = getWetherByCity(city); //把天气信息显示在title上 setTitle(weather); } }.start(); }
但是很不幸,你会发现Android会提示程序由于异常而终止。为什么在其他平台上看起来很简单的代码在Android上运行的时候依然会出错呢?假如你观察LogCat中打印的日志信息就会发现这样的错误日志:
android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
从错误信息不难看出Android禁止其他子线程来更新由UI thread创建的试图。本例中显示天气信息的title实际是就是一个由UI thread所创建的TextView,所以参试在一个子线程中去更改TextView的时候就出错了。这显示违反了单线程模型的原则:Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行
2.2 Message Queue
在单线程模型下,为了解决类似的问题,Android设计了一个Message Queue(消息队列),线程间可以通过该Message Queue并结合Handler和Looper组件进行信息交换。下面将对它们进行分别介绍:
l Message Queue
Message Queue是一个消息队列,用来存放通过Handler发布的消息。消息队列通常附属于某一个创建它的线程,可以通过Looper.myQueue()得到当前线程的消息队列。Android在第一启动程序时会默认会为UI thread创建一个关联的消息队列,用来管理程序的一些上层组件,activities,broadcast receivers 等等。你可以在自己的子线程中创建Handler与UI thread通讯。
l Handler
通过Handler你可以发布或者处理一个消息或者是一个Runnable的实例。没个Handler都会与唯一的一个线程以及该线程的消息队列管理。当你创建一个新的Handler时候,默认情况下,它将关联到创建它的这个线程和该线程的消息队列。也就是说,假如你通过Handler发布消息的话,消息将只会发送到与它关联的这个消息队列,当然也只能处理该消息队列中的消息。
主要的方法有:
1) public final boolean sendMessage(Message msg)
把消息放入该Handler所关联的消息队列,放置在所有当前时间前未被处理的消息后。
2) public void handleMessage(Message msg)
关联该消息队列的线程将通过调用Handler的handleMessage方法来接收和处理消息,通常需要子类化Handler来实现handleMessage。
l Looper
Looper扮演着一个Handler和消息队列之间通讯桥梁的角色。程序组件首先通过Handler把消息传送给Looper,Looper把消息放入队列。Looper也把消息队列里的消息广播给所有的Handler,Handler接受到消息后调用handleMessage进行处理。
1) 可以通过Looper类的静态方法Looper.myLooper得到当前线程的Looper实例,假如当前线程未关联一个Looper实例,该方法将返回空。
2) 可以通过静态方法Looper. getMainLooper方法得到主线程的Looper实例
线程,消息队列,Handler,Looper之间的关系可以通过一个图来展现:
在了解了消息队列及其相关组件的设计思想后,我们将把天气预告的案例通过消息队列来重新实现:
在了解了消息队列及其相关组件的设计思想后,我们将把天气预告的案例通过消息队列来重新实现:
private EditText editText; private Handler messageHandler; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); editText = (EditText) findViewById(R.id.weather_city_edit); Button button = (Button) findViewById(R.id.goQuery); button.setOnClickListener(this); //得到当前线程的Looper实例,由于当前线程是UI线程也可以通过Looper.getMainLooper()得到 Looper looper = Looper.myLooper(); //此处甚至可以不需要设置Looper,因为 Handler默认就使用当前线程的Looper messageHandler = new MessageHandler(looper); } @Override public void onClick(View v) { //创建一个子线程去做耗时的网络连接工作 new Thread() { @Override public void run() { //活动用户输入的城市名称 String city = editText.getText().toString(); //调用Google 天气API查询指定城市的当日天气情况 String weather = getWetherByCity(city); //创建一个Message对象,并把得到的天气信息赋值给Message对象 Message message = Message.obtain(); message.obj = weather; //通过Handler发布携带有天气情况的消息 messageHandler.sendMessage(message); } }.start(); } //子类化一个Handler class MessageHandler extends Handler { public MessageHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { //处理收到的消息,把天气信息显示在title上 setTitle((String) msg.obj); } }
通过消息队列改写过后的天气预告程序已经可以成功运行,因为Handler的handleMessage方法实际是由关联有该消息队列的UI thread调用,而在UI thread中更新title并没有违反Android的单线程模型的原则。
Valgrind 是一款 Linux下(支持 x86、x86_64和ppc32)程序的内存调试工具,它可以对编译后的二进制程序进行内存使用监测(C语言中的malloc和free,以及C++中的new和delete),找出内存泄漏问题。
Valgrind 中包含的 Memcheck 工具可以检查以下的程序错误:
使用未初始化的内存 (Use of uninitialised memory)
使用已经释放了的内存 (Reading/writing memory after it has been free’d)
使用超过malloc分配的内存空间(Reading/writing off the end of malloc’d blocks)
对堆栈的非法访问 (Reading/writing inappropriate areas on the stack)
申请的空间是否有释放 (Memory leaks – where pointers to malloc’d blocks are lost forever)
malloc/free/new/delete申请和释放内存的匹配(Mismatched use of malloc/new/new [] vs free/delete/delete [])
src和dst的重叠(Overlapping src and dst pointers in memcpy() and related functions)
重复free
1、编译安装 Valgrind:
wget http://valgrind.org/downloads/valgrind-3.4.1.tar.bz2 tar xvf valgrind-3.4.1.tar.bz2 cd valgrind-3.4.1/ ./configure --prefix=/usr/local/webserver/valgrind make make install
2、使用示例:对“ls”程序进程检查,返回结果中的“definitely lost: 0 bytes in 0 blocks.”表示没有内存泄漏。
[root@xoyo42 /]#/usr/local/webserver/valgrind/bin/valgrind --tool=memcheck --leak-check=full ls / ==1157== Memcheck, a memory error detector. ==1157== Copyright (C) 2002-2008, and GNU GPL'd, by Julian Seward et al. ==1157== Using LibVEX rev 1884, a library for dynamic binary translation. ==1157== Copyright (C) 2004-2008, and GNU GPL'd, by OpenWorks LLP. ==1157== Using valgrind-3.4.1, a dynamic binary instrumentation framework. ==1157== Copyright (C) 2000-2008, and GNU GPL'd, by Julian Seward et al. ==1157== For more details, rerun with: -v ==1157== bin data0 dev home lib64 media mnt opt root selinux sys tcsql.db.idx.pkey.dec ttserver.pid var boot data1 etc lib lost+found misc net proc sbin srv tcsql.db tmp usr ==1157== ==1157== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 5 from 1) ==1157== malloc/free: in use at exit: 28,471 bytes in 36 blocks. ==1157== malloc/free: 166 allocs, 130 frees, 51,377 bytes allocated. ==1157== For counts of detected errors, rerun with: -v ==1157== searching for pointers to 36 not-freed blocks. ==1157== checked 174,640 bytes. ==1157== ==1157== LEAK SUMMARY: ==1157== definitely lost: 0 bytes in 0 blocks. ==1157== possibly lost: 0 bytes in 0 blocks. ==1157== still reachable: 28,471 bytes in 36 blocks. ==1157== suppressed: 0 bytes in 0 blocks. ==1157== Reachable blocks (those to which a pointer was found) are not shown. ==1157== To see them, rerun with: --leak-check=full --show-reachable=yes
3、使用示例:对一个使用libevent库编写的“httptest”程序进程检查,返回结果中的“definitely lost: 255 bytes in 5 blocks.”表示发生内存泄漏。
[root@xoyo42 tcsql-0.1]# /usr/local/webserver/valgrind/bin/valgrind --tool=memcheck --leak-check=full ./httptest ==1274== Memcheck, a memory error detector. ==1274== Copyright (C) 2002-2008, and GNU GPL'd, by Julian Seward et al. ==1274== Using LibVEX rev 1884, a library for dynamic binary translation. ==1274== Copyright (C) 2004-2008, and GNU GPL'd, by OpenWorks LLP. ==1274== Using valgrind-3.4.1, a dynamic binary instrumentation framework. ==1274== Copyright (C) 2000-2008, and GNU GPL'd, by Julian Seward et al. ==1274== For more details, rerun with: -v ==1274== ==1274== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 1005 from 2) ==1274== malloc/free: in use at exit: 402,291 bytes in 74 blocks. ==1274== malloc/free: 15,939 allocs, 15,865 frees, 6,281,523 bytes allocated. ==1274== For counts of detected errors, rerun with: -v ==1274== searching for pointers to 74 not-freed blocks. ==1274== checked 682,468,160 bytes. ==1274== ==1274== 255 bytes in 5 blocks are definitely lost in loss record 17 of 32 ==1274== at 0x4A05FBB: malloc (vg_replace_malloc.c:207) ==1274== by 0x3C1D809BC6: evhttp_decode_uri (http.c:2105) ==1274== by 0x401C75: tcsql_handler (in /data0/tcsql/cankao/tcsql-0.1/tcsql) ==1274== by 0x3C1D80C88F: evhttp_get_body (http.c:1582) ==1274== by 0x3C1D8065F7: event_base_loop (event.c:392) ==1274== by 0x403E2F: main (in /data0/tcsql/cankao/tcsql-0.1/tcsql) ==1274== ==1274== LEAK SUMMARY: ==1274== definitely lost: 255 bytes in 5 blocks. ==1274== possibly lost: 0 bytes in 0 blocks. ==1274== still reachable: 402,036 bytes in 69 blocks. ==1274== suppressed: 0 bytes in 0 blocks. ==1274== Reachable blocks (those to which a pointer was found) are not shown. ==1274== To see them, rerun with: --leak-check=full --show-reachable=yes
检查httptest程序,发现有一处“char *decode_uri = evhttp_decode_uri(evhttp_request_uri(req));”中的“decode_uri”没有被free,再程序处理完成后加上“free(decode_uri);”后,再使用Valgrind检查,结果已经是“definitely lost: 0 bytes in 0 blocks.”。
将下列脚本保存为可执行脚本文件,比如叫traff.sh。
1、本脚本可自定义欲查看接口,精确到小数,并可根据流量大小灵活显示单位。
2、此脚本的采集间隔为1秒。
3、此脚本不需要额外再安装软件,可在急用情况下应付一下,比如临时想看一下是否有流量通过,大概为多少等。
4、一些流量查看软件由于计算的精确度不同,所以与此脚本显示的数值不可能一致,此脚本的显示结果与du meter对比过,相差很小。还有就是传输工具本身显示的传输速度并不准确。
用法为:
1、chmod +x ./traff.sh 将文件改成可执行脚本。
2、./traff.sh eth0即可开始监看接口eth0流量,按ctrl+c退出。
#!/bin/bash while [ "1" ] do eth=$1 RXpre=$(cat /proc/net/dev | grep $eth | tr : " " | awk '{print $2}') TXpre=$(cat /proc/net/dev | grep $eth | tr : " " | awk '{print $10}') sleep 1 RXnext=$(cat /proc/net/dev | grep $eth | tr : " " | awk '{print $2}') TXnext=$(cat /proc/net/dev | grep $eth | tr : " " | awk '{print $10}') clear echo -e "\t RX `date +%k:%M:%S` TX" RX=$((${RXnext}-${RXpre})) TX=$((${TXnext}-${TXpre})) if [[ $RX -lt 1024 ]];then RX="${RX}B/s" elif [[ $RX -gt 1048576 ]];then RX=$(echo $RX | awk '{print $1/1048576 "MB/s"}') else RX=$(echo $RX | awk '{print $1/1024 "KB/s"}') fi if [[ $TX -lt 1024 ]];then TX="${TX}B/s" elif [[ $TX -gt 1048576 ]];then TX=$(echo $TX | awk '{print $1/1048576 "MB/s"}') else TX=$(echo $TX | awk '{print $1/1024 "KB/s"}') fi echo -e "$eth \t $RX $TX " done
在c++中new的对象,如果不返回java,必须用release掉,否则内存泄露。包括NewStringUTF,NewObject
。如果返回java不必release,java会自己回收。
jstring jstr = env->NewStringUTF((*p).sess_id);
…
env->DeleteLocalRef( jstr);
jobject jobj = env->NewObject(clazz,midInit);
return jobj;
内存泄露可以先从windows资源管理器中,看到随程序运行,内存不断增长的趋势,具体可以用hp jmeter检测。在运行程序时,加jvm参数 -Xrunhprof:heap=all,cutoff=0 ,生成java.hprof.txt,用jmeter打开,Metric -> Residual Objects (Count),可以看到未回收的对象,选中要查看的对象,点Mark记录下要查看的对象,Window -> New Window 打开新窗口,用Metric -> Reference Graph Tree,然后点Find Immediately可以看到对象被哪里引用。
找出内存泄漏另一方法
程序有内存泄漏的第一个迹象通常是它抛出一个
有工具可以利用 GC 日志输出并以图形方式将它显示出来,JTune 就是这样的一种工具(请参阅 参考资料)。观察 GC 之后堆大小的图,可以看到程序内存使用的趋势。对于大多数程序来说,可以将内存使用分为两部分:baseline 使用和current load 使用。对于服务器应用程序,baseline 使用就是应用程序在没有任何负荷、但是已经准备好接受请求时的内存使用,current load 使用是在处理请求过程中使用的、但是在请求处理完成后会释放的内存。只要负荷大体上是恒定的,应用程序通常会很快达到一个稳定的内存使用水平。如果在应用程序已经完成了其初始化并且负荷没有增加的情况下,内存使用持续增加,那么程序就可能在处理前面的请求时保留了生成的对象。
图 1 显示 GC 之后应用程序堆大小随着时间的变化图。上升趋势是存在内存泄漏的警示信号。(在真实的应用程序中,坡度不会这么大,但是在收集了足够长时间的 GC 数据后,上升趋势通常会表现得很明显。)
图 1. 持续上升的内存使用趋势
确信有了内存泄漏后,下一步就是找出哪种对象造成了这个问题。所有内存分析器都可以生成按照对象类进行分解的堆快照。有一些很好的商业堆分析工具,但是找出内存泄漏不一定要花钱买这些工具 —— 内置的
清单 3 显示分解了应用程序内存使用的
请参阅 清单 3。
清单 4 展示了
清单 4. HPROF 输出,显示 Map.Entry 对象的分配点
|
另外
jstring jstr = (jstring)env->CallObjectMethod(authenRequest, mid_authenReq_getSdId_S);
env->GetStringUTFRegion(jstr,0,env->GetStringLength(jstr),authenReq.sd_id);
当jstr是null时,env->GetStringLength(jstr)会出错,导致jvm崩溃
FROM: http://www.blogjava.net/neumqp/archive/2006/03/02/33152.html