在JNI编程中避免内存泄漏

JAVA中的内存泄漏

JAVA编程中的内存泄漏,从泄漏的内存位置角度可以分为两种:JVMJava Heap的内存泄漏;JVM内存中native memory的内存泄漏。

Java Heap的内存泄漏

Java对象存储在JVM进程空间中的Java Heap中,Java Heap可以在JVM运行过程中动态变化。如果Java对象越来越多,占据Java Heap的空间也越来越大,JVM会在运行时扩充Java Heap的容量。如果Java Heap容量扩充到上限,并且在GC后仍然没有足够空间分配新的Java对象,便会抛出out of memory异常,导致JVM进程崩溃。

Java Heapout of memory异常的出现有两种原因——①程序过于庞大,致使过多Java对象的同时存在;②程序编写的错误导致Java Heap内存泄漏。

多种原因可能导致Java Heap内存泄漏。JNI编程错误也可能导致Java Heap的内存泄漏。

JVMnative memory的内存泄漏

从操作系统角度看,JVM在运行时和其它进程没有本质区别。在系统级别上,它们具有同样的调度机制,同样的内存分配方式,同样的内存格局。

JVM进程空间中,Java Heap以外的内存空间称为JVMnative memory。进程的很多资源都是存储在JVMnative memory中,例如载入的代码映像,线程的堆栈,线程的管理控制块,JVM的静态数据、全局数据等等。也包括JNI程序中native code分配到的资源。

JVM运行中,多数进程资源从native memory中动态分配。当越来越多的资源在native memory中分配,占据越来越多native memory空间并且达到native memory上限时,JVM会抛出异常,使JVM进程异常退出。而此时Java Heap往往还没有达到上限。

多种原因可能导致JVMnative memory内存泄漏。例如JVM在运行中过多的线程被创建,并且在同时运行。JVM为线程分配的资源就可能耗尽native memory的容量。

JNI编程错误也可能导致native memory的内存泄漏。对这个话题的讨论是本文的重点。


JNI编程中明显的内存泄漏

JNI编程实现了native codeJava程序的交互,因此JNI代码编程既遵循 native code编程语言的编程规则,同时也遵守JNI编程的文档规范。在内存管理方面,native code编程语言本身的内存管理机制依然要遵循,同时也要考虑JNI编程的内存管理。

本章简单概括JNI编程中显而易见的内存泄漏。从native code编程语言自身的内存管理,和JNI规范附加的内存管理两方面进行阐述。

Native Code本身的内存泄漏

JNI编程首先是一门具体的编程语言,或者C语言,或者C++,或者汇编,或者其它native的编程语言。每门编程语言环境都实现了自身的内存管理机制。因此,JNI程序开发者要遵循native语言本身的内存管理机制,避免造成内存泄漏。以C语言为例,当用malloc()在进程堆中动态分配内存时,JNI程序在使用完后,应当调用free()将内存释放。总之,所有在native语言编程中应当注意的内存泄漏规则,在JNI编程中依然适应。

Native语言本身引入的内存泄漏会造成native memory的内存,严重情况下会造成native memoryout of memory

Global Reference引入的内存泄漏

JNI编程还要同时遵循JNI的规范标准,JVM附加了JNI编程特有的内存管理机制。

JNI中的Local Reference只在native method执行时存在,当native method执行完后自动失效。这种自动失效,使得对Local Reference的使用相对简单,native method执行完后,它们所引用的Java对象的reference count会相应减1。不会造成Java HeapJava对象的内存泄漏。

Global ReferenceJava对象的引用一直有效,因此它们引用的Java对象会一直存在Java Heap中。程序员在使用Global Reference时,需要仔细维护对Global Reference的使用。如果一定要使用Global Reference,务必确保在不用的时候删除。就像在C语言中,调用malloc()动态分配一块内存之后,调用free()释放一样。否则,Global Reference引用的Java对象将永远停留在Java Heap中,造成Java Heap的内存泄漏。


JNI编程中潜在的内存泄漏——对LocalReference的深入理解

Local Referencenative method执行完成后,会自动被释放,似乎不会造成任何的内存泄漏。但这是错误的。对Local Reference的理解不够,会造成潜在的内存泄漏。

本章重点阐述Local Reference使用不当可能引发的内存泄漏。引入两个错误实例,也是JNI程序员容易忽视的错误;在此基础上介绍Local Reference表,对比native method中的局部变量和JNI Local Reference的不同,使读者深入理解JNI Local Reference的实质;最后为JNI程序员提出应该如何正确合理使用JNI Local Reference,以避免内存泄漏。

错误实例 1

在某些情况下,我们可能需要在native method里面创建大量的JNI Local Reference。这样可能导致 native memory的内存泄漏,如果在native method返回之前native memory已经被用光,就会导致native memoryout of memory

在代码清单 1 里,我们循环执行count次,JNI functionNewStringUTF()在每次循环中从Java Heap中创建一个String对象,strJava Heap传给JNI native methodLocal Reference,每次循环中新创建的String对象覆盖上次循环中str的内容。str似乎一直在引用到一个String对象。整个运行过程中,我们看似只创建一个Local Reference

执行代码清单1的程序,第一部分为Java代码,nativeMethod(int i)中,输入参数设定循环的次数。第二部分为JNI代码,用C语言实现了nativeMethod(int i)

清单 1. Local Reference引发内存泄漏

运行结果证明,JVM运行异常终止,原因是创建了过多的Local Reference,从而导致out of memory。实际上,nativeMethod在运行中创建了越来越多的JNI Local Reference,而不是看似的始终只有一个。过多的Local Reference,导致了JNI内部的JNI Local Reference表内存溢出。

错误实例 2

实例 2 是实例 1 的变种,Java代码未作修改,但是nativeMethod(int i)C语言实现稍作修改。在JNInative method中实现的utility函数中创建JavaString对象。utility函数只建立一个String对象,返回给调用函数,但是utility函数对调用者的使用情况是未知的,每个函数都可能调用它,并且同一函数可能调用它多次。在实例 2 中,nativeMethod在循环中调用count次,utility函数在创建一个String对象后即返回,并且会有一个退栈过程,似乎所创建的Local Reference会在退栈时被删除掉,所以应该不会有很多Local Reference被创建。实际运行结果并非如此。

清单 2. Local Reference引发内存泄漏

运行结果证明,实例 2 的结果与实例 1 的完全相同。过多的Local Reference被创建,仍然导致了JNI内部的 JNI Local Reference表内存溢出。实际上,在utility函数CreateStringUTF(JNIEnv * env)

执行完成后的退栈过程中,创建的Local Reference并没有像native code中的局部变量那样被删除,而是继续在Local Reference表中存在,并且有效。Local Reference 和局部变量有着本质的区别。

Local Reference深层解析

Java JNI的文档规范只描述了JNI Local Reference是什么(存在的目的),以及应该怎么使用Local Reference(开放的接口规范)。但是对Java虚拟机中JNI Local Reference的实现并没有约束,不同的Java虚拟机有不同的实现机制。这样的好处是,不依赖于具体的JVM实现,有好的可移植性;并且开发简单,规定了“应该怎么做、怎么用”。但是弊端是初级开发者往往看不到本质,“不知道为什么这样做”。对Local Reference没有深层的理解,就会在编程过程中无意识的犯错。

Local ReferenceLocal Reference

理解Local Reference表的存在是理解JNI Local Reference的关键。

JNI Local Reference的生命期是在native method的执行期(从Java程序切换到native code环境时开始创建,或者在native method执行时调用JNI function创建),在 native method执行完毕切换回Java程序时,所有JNI Local Reference被删除,生命期结束(调用JNI function可以提前结束其生命期)。

实际上,每当线程从Java环境切换到native code上下文时(J2N),JVM会分配一块内存,创建一个Local Reference表,这个表用来存放本次native method执行中创建的所有的Local Reference。每当在native code中引用到一个Java对象时,JVM就会在这个表中创建一个Local Reference。比如,实例 1 中我们调用NewStringUTF()Java Heap中创建一个String对象后,在Local Reference表中就会相应新增一个Local Reference

图 1. Local Reference表、Local ReferenceJava对象的关系

image003

图 1 中:

⑴运行native method的线程的堆栈记录着Local Reference表的内存位置(指针 p)。

Local Reference表中存放JNI Local Reference,实现Local ReferenceJava对象的映射。

native method代码间接访问Java对象(java obj1,java obj2)。通过指针p定位相应的Local Reference的位置,然后通过相应的Local Reference映射到Java对象。

⑷ 当native method引用一个Java对象时,会在Local Reference表中创建一个新Local Reference。在Local Reference结构中写入内容,实现Local ReferenceJava对象的映射。

native method调用DeleteLocalRef()释放某个JNI Local Reference时,首先通过指针p定位相应的Local ReferenceLocal Ref表中的位置,然后从Local Ref表中删除该Local Reference,也就取消了对相应Java对象的引用(Ref count1)。

⑹当越来越多的Local Reference被创建,这些Local Reference会在Local Ref表中占据越来越多内存。当Local Reference太多以至于Local Ref表的空间被用光,JVM会抛出异常,从而导致JVM的崩溃。

Local Ref不是native code的局部变量

很多人会误将JNI中的Local Reference理解为Native Code的局部变量。这是错误的。

Native Code的局部变量和Local Reference是完全不同的,区别可以总结为:

⑴局部变量存储在线程堆栈中,而Local Reference存储在Local Ref表中。

⑵局部变量在函数退栈后被删除,而Local Reference在调用DeleteLocalRef()后才会从Local Ref表中删除,并且失效,或者在整个Native Method执行结束后被删除。

⑶ 可以在代码中直接访问局部变量,而Local Reference的内容无法在代码中直接访问,必须通过JNI function间接访问。JNI function实现了对Local Reference的间接访问,JNI function的内部实现依赖于具体JVM

代码清单 1 中 str = (*env)->NewStringUTF(env, "0");

strjstring类型的局部变量。Local Ref表中会新创建一个Local Reference,引用到NewStringUTF(env, "0")Java Heap中新建的String对象。如图 2 所示:

图 2. str 间接引用 string 对象image005

图 2 中,str 是局部变量,在 native method 堆栈中。Local Ref3 是新创建的 Local Reference,在 Local Ref 表中,引用新创建的 String 对象。JNI 通过 str 和指针 p 间接定位 Local Ref3,但 p 和 Local Ref3 对 JNI 程序员不可见。

Local Reference导致内存泄漏

在以上论述基础上,我们通过分析错误实例 1 和实例 2,来分析 Local Reference 可能导致的内存泄漏,加深对 Local Reference 的深层理解。

分析错误实例 1:

局部变量str在每次循环中都被重新赋值,间接指向最新创建的Local Reference,前面创建的Local Reference 一直保留在 Local Ref 表中。

在实例 1 执行完第i次循环后,内存布局如图 3:

图 3. 执行i次循环后的内存布局

image007

继续执行完第i+1次循环后,内存布局发生变化,如图 4:

图 4. 执行i+1次循环后的内存布局

image009

图 4 中,局部变量 str 被赋新值,间接指向了Local Ref i+1。在native method运行过程中,我们已经无法释放Local Ref i占用的内存,以及Local Ref i所引用的第istring对象所占据的Java Heap内存。所以,native memoryLocal Ref i被泄漏,Java Heap中创建的第 istring对象被泄漏了。

也就是说在循环中,前面创建的所有iLocal Reference都泄漏了native memory的内存,创建的所有 i 个string对象都泄漏了Java Heap的内存。

直到native memory执行完毕,返回到Java程序时(N2J),这些泄漏的内存才会被释放,但是 Local Reference 表所分配到的内存往往很小,在很多情况下N2J之前可能已经引发严重内存泄漏,导致Local Reference表的内存耗尽,使JVM崩溃,例如错误实例 1。

分析错误实例 2:

实例 2 与实例 1 相似,虽然每次循环中调用工具函数CreateStringUTF(env)来创建对象,但是在CreateStringUTF(env)返回退栈过程中,只是局部变量被删除,而每次调用创建的Local Reference仍然存在Local Ref表中,并且有效引用到每个新创建的string对象。str局部变量在每次循环中被赋新值。

这样的内存泄漏是潜在的,但是这样的错误 在JNI程序员编程过程中却经常出现。通常情况,在触发out of memory之前,native method已经执行完毕,切换回 Java 环境,所有 Local Reference 被删除,问题也就没有显露出来。但是某些情况下就会引发out of memory,导致实例 1 和实例 2 中的JVM崩溃。

控制Local Reference生命期

因此,在JNI编程时,正确控制JNILocal Reference的生命期。如果需要创建过多的Local Reference,那么在对被引用的Java对象操作结束后,需要调用JNI function(如 DeleteLocalRef()),及时将JNILocal ReferenceLocal Ref表中删除,以避免潜在的内存泄漏。

总结


本文阐述了JNI编程可能引发的内存泄漏,JNI编程既可能引发Java Heap的内存泄漏,也可能引发native memory的内存泄漏,严重的情况可能使JVM运行异常终止。JNI软件开发人员在编程中,应当考虑以下几点,避免内存泄漏:

  • native code本身的内存管理机制依然要遵循。
  • 使用Global reference时,当native code不再需要访问Global reference时,应当调用JNI函数DeleteGlobalRef()删除Global reference和它引用的Java对象。Global reference管理不当会导致Java Heap的内存泄漏。
  • 透彻理解Local reference,区分Local referencenative code的局部变量,避免混淆两者所引起的native memory的内存泄漏。
  • 使用Local reference时,如果Local reference引用了大的Java对象,当不再需要访问Local reference时,应当调用JNI函数DeleteLocalRef()删除Local reference,从而也断开对Java对象的引用。这样可以避免Java Heapout of memory
  • 使用Local reference时,如果在native method执行期间会创建大量的Local reference,当不再需要访问Local reference时,应当调用JNI函数DeleteLocalRef()删除Local referenceLocal reference表空间有限,这样可以避免Local reference表的内存溢出,避免native memoryout of memory
  • 严格遵循JavaJNI规范书中的使用规则。

参考链接


在 JNI 编程中避免内存泄漏

JNI Local Reference Changes in ICS

[This post is by Elliott Hughes, a Software Engineer on the Dalvik team. — Tim Bray]

If you don’t write native code that uses JNI, you can stop reading now. If you do write native code that uses JNI, you really need to read this.

What’s changing, and why?

Every developer wants a good garbage collector. The best garbage collectors move objects around. This lets them offer very cheap allocation and bulk deallocation, avoids heap fragmentation, and may improve locality. Moving objects around is a problem if you’ve handed out pointers to them to native code. JNI uses types such as jobject to solve this problem: rather than handing out direct pointers, you’re given an opaque handle that can be traded in for a pointer when necessary. By using handles, when the garbage collector moves an object, it just has to update the handle table to point to the object’s new location. This means that native code won’t be left holding dangling pointers every time the garbage collector runs.

In previous releases of Android, we didn’t use indirect handles; we used direct pointers. This didn’t seem like a problem as long as we didn’t have a garbage collector that moves objects, but it let you write buggy code that still seemed to work. In Ice Cream Sandwich, even though we haven't yet implemented such a garbage collector, we've moved to indirect references so you can start detecting bugs in your native code.

Ice Cream Sandwich features a JNI bug compatibility mode so that as long as your AndroidManifest.xml’s targetSdkVersion is less than Ice Cream Sandwich, your code is exempt. But as soon as you update your targetSdkVersion, your code needs to be correct.

CheckJNI has been updated to detect and report these errors, and in Ice Cream Sandwich, CheckJNI is on by default if debuggable="true" in your manifest.

A quick primer on JNI references

In JNI, there are several kinds of reference. The two most important kinds are local references and global references. Any given jobject can be either local or global. (There are weak globals too, but they have a separate type, jweak, and aren’t interesting here.)

The global/local distinction affects both lifetime and scope. A global is usable from any thread, using that thread’s JNIEnv*, and is valid until an explicit call to DeleteGlobalRef(). A local is only usable from the thread it was originally handed to, and is valid until either an explicit call to DeleteLocalRef() or, more commonly, until you return from your native method. When a native method returns, all local references are automatically deleted.

In the old system, where local references were direct pointers, local references were never really invalidated. That meant you could use a local reference indefinitely, even if you’d explicitly called DeleteLocalRef() on it, or implicitly deleted it with PopLocalFrame()!

Although any given JNIEnv* is only valid for use on one thread, because Android never had any per-thread state in a JNIEnv*, it used to be possible to get away with using a JNIEnv* on the wrong thread. Now there’s a per-thread local reference table, it’s vital that you only use a JNIEnv* on the right thread.

Those are the bugs that ICS will detect. I’ll go through a few common cases to illustrate these problems, how to spot them, and how to fix them. It’s important that you do fix them, because it’s likely that future Android releases will utilize moving collectors. It will not be possible to offer a bug-compatibility mode indefinitely.

Common JNI reference bugs

Bug: Forgetting to call NewGlobalRef() when stashing a jobject in a native peer

If you have a native peer (a long-lived native object corresponding to a Java object, usually created when the Java object is created and destroyed when the Java object’s finalizer runs), you must not stash a jobject in that native object, because it won’t be valid next time you try to use it. (Similar is true of JNIEnv*s. They might be valid if the next native call happens on the same thread, but they won’t be valid otherwise.)

The fix for this is to only store JNI global references. Because there’s never any automatic cleanup of JNI global references, it’s critically important that you clean them up yourself. This is made slightly awkward by the fact that your destructor won’t have a JNIEnv*. The easiest fix is usually to have an explicit ‘destroy‘ function for your native peer, called from the Java peer’s finalizer:

You should always have matching calls to NewGlobalRef()/DeleteGlobalRef(). CheckJNI will catch global reference leaks, but the limit is quite high (2000 by default), so watch out.

If you do have this class of error in your code, the crash will look something like this:

If you’re using another thread’s JNIEnv*, the crash will look something like this:

Bug: Mistakenly assuming FindClass() returns global references

FindClass() returns local references. Many people assume otherwise. In a system without class unloading (like Android), you can treat jfieldID and jmethodID as if they were global. (They’re not actually references, but in a system with class unloading there are similar lifetime issues.) But jclass is a reference, and FindClass() returns local references. A common bug pattern is “static jclass”. Unless you’re manually turning your local references into global references, your code is broken. Here’s what correct code should look like:

If you do have this class of error in your code, the crash will look something like this:

Bug: Calling DeleteLocalRef() and continuing to use the deleted reference

It shouldn’t need to be said that it’s illegal to continue to use a reference after calling DeleteLocalRef() on it, but because it used to work, so you may have made this mistake and not realized. The usual pattern seems to be where native code has a long-running loop, and developers try to clean up every single local reference as they go to avoid hitting the local reference limit, but they accidentally also delete the reference they want to use as a return value!

The fix is trivial: don’t call DeleteLocalRef() on a reference you’re going to use (where “use” includes “return”).

Bug: Calling PopLocalFrame() and continuing to use a popped reference

This is a more subtle variant of the previous bug. The PushLocalFrame() and PopLocalFrame() calls let you bulk-delete local references. When you call PopLocalFrame(), you pass in the one reference from the frame that you’d like to keep (typically for use as a return value), or NULL. In the past, you’d get away with incorrect code like the following:

The fix is generally to pass the reference to PopLocalFrame(). Note in the above example that you don’t need to keep references to the individual array elements; as long as the GC knows about the array itself, it’ll take care of the elements (and any objects they point to in turn) itself.

If you do have this class of error in your code, the crash will look something like this:

Wrapping up

Yes, we asking for a bit more attention to detail in your JNI coding, which is extra work. But we think that you’ll come out ahead on the deal as we roll in better and more sophisticated memory management code.

原文链接


JNI Local Reference Changes in ICS
Android冰淇淋三明治ICS(4.0+)JNI局部引用的变化

adb shell下向应用发送GC命令

DDMS里有个Cause GC命令,用来要求虚拟机强制GC,如下图:
1329cc2bed09b4fdd38b63d6440b1fb9
但是在有些环境下面,测试同学,或者用户机器上面没有安装Android Sdk,这个时候使用adb shell来执行GC命令是比较合适的,但是网上搜索了一下,发现adb shell下没有提供这个命令。

根据网上介绍,执行GC命令实际上是通过kill命令向进程发送了数字为10的自定义信号而已,代码如下:

相同道理,我们只要直接在adb shell里面直接向进程发送数字为10的自定义信号就可以了。

参考链接


关于使用 adb 命令触发 GC 操作的问题

C/C++中的正则表达式库——PCRE, PCRE++

1. 什么是PCRE? 什么是PCRE++?
PCRE,全称是Perl Compatible Regular Expressions。从名字我们可以看出PCRE库是与Perl中正则表达式相兼容的一个正则表达式库。PCRE是免费开源的库,它是由C语言实现的,这里是它的官方主页:http://www.pcre.org/,感兴趣的朋友可以在这里了解更多的内容。
要得到PCRE库,可以从这里下载:http://sourceforge.net/projects/pcre/files/

PCRE++是一个对PCRE库的C++封装,它提供了更加方便、易用的C++接口。这里是它的官方主页:http://www.daemon.de/PCRE,感兴趣的朋友可以在这里了解更多的内容。
要得到PCRE++库,可以从这里下载:http://www.daemon.de/PcreDownload

2. PCRE接口介绍
(1). pcre_compile

(2). pcre_exec

3. PCRE++接口介绍
PCRE++PCRE库封装成了两个类,一个是RE_Options, 用来指定匹配选项,一个是RE,用来提供匹配相关的接口。RE_options类在这里我就不介绍了,我主要介绍一下RE类:
(1)RE的构造函数传入正则表达式,并在构造函数中调用Init函数,将该正则表达进行编译
(2)REpattern()成员用来得到初始传入的正则表达式字符串
(3)REerror()成员用来得到匹配过程中的出错信息
(4)REFullMatch()成员用来判断某字符串整体是否匹配指定正则表达式
(5)REPartialMatch()成员用来判断某字符串的部分是否匹配指定正则表达式

4. PCRE/PCRE++使用注意事项
(1)使用pcre请包含pcre.h头文件
(2)使用pcre_compile, pcre_exec后,记得调用pcre_free释放内存,以免造成内存泄露
(3)使用pcre编译的时候需要依赖libpcre.a
(4)使用pcre++请包含pcrecpp.h头文件
(5)使用pcre++RE类的析构函数会自动释放相关内存,因此不用担心内存泄露
(6)使用pcre++编译的时候需要依赖libpcrecpp.a
(7)使用pcrecpp要使用pcrecpp命名空间

5. PCRE使用举例
下面是例程:

下面是运行结果:

6. PCRE++使用举例
下面是例程:

下面是运行结果:

参考链接


深入浅出C/C++中的正则表达式库(三)——PCRE, PCRE++

js文件中获取${pageContext.request.contextPath}

一般从JSP文件中,可以直接使用${pageContext.request.contextPath}非常方便的获得当前页面的路径,用来处理被Apache2代理之后出现URL变化的问题,比如增加了某些前缀,方便转发,即使是JSP内嵌的JavaScript脚本,也是可以如此操作。但是如果是一个独立的JavaScript文件,通过

这样的方式引入,则在JavaScript文件内部是无法直接调用${pageContext.request.contextPath}获取前缀的,因为${pageContext.request.contextPath}是需要JSP文件处理的变量,而对于独立的JavaScript文件,默认是不做任何处理的。

因此这个变量只能是通过某个全局变量传递到JavaScript文件中。
目前比较有效的实现方法是通过设置一个隐藏的文本框的方式来处理。

JavaScript文件中获取这个变量的方法如下:

保护wp-login.php后台登陆入口

最近在观察后台的登陆统计的时候,发现来自"乌克兰","美国"的某个IP的访问数量特别多,访问记录显示是大量访问登陆页面https://www.mobibrw.com/wp-login.php,明显是发生了密码破解攻击。

TopVisitors

login-recorders

本来以为WordPress 4.4版本已经对密码的访问次数进行了限制,结果试了一下,发现完全没有任何限制,很容易导致密码穷举攻击。比较简单的解决方法是对登陆页面进行密码尝试次数限制。

最方便的是安装Limit Login Attempts插件即可,这个插件很久不更新了。目前从测试来看,与WordPress 4.4版本是兼容的。

Ubuntu 14.04隐藏Apache-2.4的版本号与操作系统类型

一般情况下,软件的漏洞信息和特定版本,特定操作系统是相关的,因此,软件的版本号以及操作系统类型对攻击者来说是很有价值的。

在默认情况下,Apache会在返回信息中把自身的版本号,操作系统类型都显示出来,如下图:
Apache2-403

这样做会造成潜在的安全风险,导致不必要的攻击行为。

Ubuntu 14.04系统上隐藏Apache-2.4的版本号与操作系统类型的方法如下:

把文件中的的ServerTokens OS修改为ServerTokens Prod,ServerSignature On修改为ServerSignature Off,如下图所示:

apache2-security-conf

修改完成后,重启Apache2的服务

修改后的结果如下图所示,已经没有系统类型信息了,仅仅返回了一个403错误。

Apache2-404-Modify

阿里云服务器从Ubuntu 12.04升级到Ubuntu 14.04

阿里云的服务器是Ubuntu 12.04根据Canonical发布的支持路线图,可以看到2017年4月份之后就不再提供支持。因此很有升级导致Ubuntu 14.04的必要,更别说很多软件在Ubuntu 12.04上已经比较过时了。

Ubuntu LTS版本支持路线图如下图:
1_201204291858511OI60

升级的流程如下所示:(执行下面操作之前,请务必先备份重要数据,阿里云服务器推荐使用自带的系统快照功能,非常好用)

1.首先保证当前系统上的软件都是最新的


2.安装系统升级模块


1-do-release-upgrade

3.升级流程


允许系统在升级期间开放1022端口用来处理系统升级异常,当系统升级异常的时候,可以通过这个端口进行某些恢复操作。(实际上没太大作用,出问题就快照回滚了,更快速安全方便)
2-ssh-port-query-yes

输入y,点击回车(Enter)。

3-iptables-add-port-press-enter

点击回车(Enter),允许在iptable上面开放1022端口出来,这个端口在安装完成后会自动关闭,不需要过多关心。

4-rewrite-sources-list-yes

允许升级程序更新sources.list用来获取升级所需要的文件,输入y,点击回车(Enter)。

5-upgrade-confirm

询问是否确认系统升级,输入y,点击回车(Enter)。

6-disable-ssh-password-no

询问是否禁止root用户通过ssh访问系统,这个一定要选择No,否则升级完成后,我们无法远程登陆系统。

7-restart-services-without-asking-yes

询问在升级期间是否允许自动重启需要升级的服务,这个一定要选择Yes,否则会不断的询问你是不是确定重启服务,非常麻烦。

8-serurity-limits-conf-replace-enter

询问是否用新系统的文件替换原系统的/etc/security/limits.conf文件,直接回车(Enter),不允许替换,使用原系统的配置。

9-etc-default-rcS-enter

同上,直接回车(Enter)。

9-etc-default-rcS-enter

同上,直接回车(Enter)。

10-etc-sysctl-conf-enter

同上,直接回车(Enter)。

11-etc-vsftpd-conf-enter

同上,直接回车(Enter)。

12-etc-php5-fpm-php-fpm-conf-enter

同上,直接回车(Enter)。

13-etc-php5-fpm-php-ini-enter

询问是否替换文件,同上,直接回车(Enter),不允许替换。

14-etc-php5-cgi-php-ini-enter

同上,直接回车(Enter)。

15-etc-init-mounted-run-conf-enter

同上,直接回车(Enter)。

16-etc-apache2-mods-available-fcgid-conf-enter

同上,直接回车(Enter)。

17-etc-sv-git-daemon-run-enter

同上,直接回车(Enter)。

18-etc-default-tomcat7-enter

同上,直接回车(Enter)。

19-upgrade-phpmyadmin-enter

询问是否升级数据库,此处选择Yes,回车(Enter)。

20-upgrade-phpmyadmin-password-enter

输入数据库的密码,完成后点击回车(Enter)。

21-remove-obsolete-packages-yes

询问是否删除不再使用的安装包,输入y后点击回车(Enter)。

22-restart-required-yes

升级完成,询问是否重启系统,输入y后点击回车(Enter)。系统会重启,远程连接会断开,需要稍等几分钟后重新连接服务器。

3.恢复被修改后的系统配置信息


安装Apache2PHP扩展libapache2-mod-php5,Ubuntu 12.04版本的库,在升级的过程中被丢弃了,需要重新手动安装。

23-after-restart-install-libapache2-mod-php5

询问是否替换已经存在的PHP配置文件,直接点击回车,不允许替换。

24-after-restart-install-libapache2-mod-php5-php-ini-enter

修改Apache2的配置文件

25-after-restart-vim-apache2-conf

原有Apache 2.2配置为:

发现升级后变更为:

导致PHP无法正常工作,因此需要修改回来。

修改前:
26-after-restart-vim-apache2-conf-IncludeOptional

修改后:27-after-restart-vim-apache2-conf-IncludeOptional-modify-complete

Apache 2.4修改了默认目录位置(这导致2.2版本设置的禁止目录流量功能失效),并且默认开启了目录浏览功能,会导致潜在的安全问题,需要手工关闭.

修改前:29-after-restart-apache2-disable-indexs

修改后:30-after-restart-apache2-disable-indexs-modify

重启Apache2

28-after-restart-apache2-restart

到此,整个系统升级完成,所有功能恢复正常。

从升级的效果来看,服务器的响应明显变快,非常值得升级!

Shared Preferences的分析

The SharedPreferences class provides a general framework that allows you to save and retrieve persistent key-value pairs of primitive data types. You can use SharedPreferences to save any primitive data: booleans, floats, ints, longs, and strings. This data will persist across user sessions (even if your application is killed).

调用getSharedPreferences()获取对应的的文件,该函数实现功能如下:

//Context类静态数据集合,以键值对保存了所有读取该xml文件后所形成的数据集合

可以看到他有一个Map, 而针对SharedPreferencesImpl里面,由会有map, 这样也就可以证明, 为什么SharedPreference被广泛使用了。 他在普通时刻,内容是从内存里面直接读取的, 只有在第一次启动时,是IO操作。

2. apply()commit()的区别

/** boolean commit();的注释如下:

apply方法的注释:

apply不同于commitcommit是同步的去更改硬盘上的东西,而apply是先直接更改内存中的, 然后异步的去更改应硬盘中的内容。

不用去担心线程安全问题, 因为如果一个其他的线程去commit,而刚好有一个还没有完成的applycommit会被阻塞到异步线程提交完成。

参考链接


Shared Preferences的分析

Android Studio中的productFlavors指定默认编译执行的任务

Android Studio中指定了productFlavors如下:

整个的build.gradle里面的内容如下:

但是这个时候我们点击Android Studio的调试按钮的时候,不知道究竟是使用哪个Flavors来编译,比如在Android Studio 1.5的时候,是按照从上到下的顺序处理的,默认是使用排在第一个的Daily,而到了Android Studio 2.1 Preview 5版本,却变成了按照字母排序,结果变成了默认编译Advance

网上搜索了一下,找到如下解决方法:

选择"Build Variant",然后在出现的窗口中选择其中一个选项作为默认的编译,运行选项即可。

BuildVariantSel

参考链接


Gradle Build only a flavour