WORDPRESS 4.5 后台/登录页面强制HTTPS

这段时间貌似服务器经常被人攻击,由于服务器已经支持HTTPS登陆,而全站SSL加密又不是那么迫切,遂考虑后台SSL加密,强制HTTPS。
设置方法如下:
修改wp-config.php

require_once(ABSPATH . 'wp-settings.php');

之前(貌似放在后面也可以),加

define('FORCE_SSL_ADMIN', true);

就能使得后台强制加密了;
而加入一行

define('FORCE_SSL_LOGIN', true);

就可以使登录页面强制加密了。

Ubuntu 14.04 关闭PHP服务器上返回的X-Powered-By信息

最近在分析网站性能的时候,发现服务器返回了"X-Powered-By"字段,这个字段中携带了PHP的版本号,系统的版本号。如下图:
X-Powered-By

而出于安全考虑,这两个信息是不应该被返回给客户端的。

Ubuntu 14.04下服务器禁止返回"X-Powered-By"的设置如下:
对于使用Apache2内置PHP的服务器,则需要修改Apache2对应目录下的配置文件

$ sudo vim /etc/php5/apache2/php.ini

对于使用PHP-FPM调度的服务器,则需要同时修改PHP-FPM,FastCGI对应目录下的配置文件

$ sudo vim /etc/php5/fpm/php.ini
$ sudo vim /etc/php5/cgi/php.ini

找到

; Decides whether PHP may expose the fact that it is installed on the server
; (e.g. by adding its signature to the Web server header).  It is no security
; threat in any way, but it makes it possible to determine whether you use PHP
; on your server or not.
; http://php.net/expose-php
expose_php = On

把其中的expose_php = On调整为:expose_php = Off,调整后的结果如下:

; Decides whether PHP may expose the fact that it is installed on the server
; (e.g. by adding its signature to the Web server header).  It is no security
; threat in any way, but it makes it possible to determine whether you use PHP
; on your server or not.
; http://php.net/expose-php
expose_php = Off

接下来,重启Apache2

$ sudo service apache2 restart

重启PHP-FPM,如果配置了的话。

$ sudo service php5-fpm restart

再次请求服务器后,发现"X-Powered-By"已经不会再返回了。

X-Powered-By-Off

安装应用失败:INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES、INSTALL_FAILED_UID_CHANGED、INSTALL_FAILED_ALREADY_EXISTS

adb install 一个apk错误:

INSTALL_FAILED_ALREADY_EXISTS

应用已存在,使用 adb install -r xx.apk 即重新安装

INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES

签名冲突,说明系统存在这个应用,且签名有冲突,先执行 adb uninstall xx.apk

但还是提示这个错误

 

如何彻底卸载掉一个apk?

去/data/system/packages.xml中,把这个包相关的行全部删掉

INSTALL_FAILED_UID_CHANGED

应用删除不干净,还有残留文件,去

/data/app(apk file), /system/app/(apk file), /data/data/(data file)几个目录下,把应用相关数据删掉。

重启

 

安装成功。

 

使用WordPress的子主题功能修改你的WordPress主题

相信很多朋友使用的WordPress主题都经过了自己的一些修改,好不容易折腾好了,主题的升级版发布了,要不要升级呢?升级以后,还得重新再次修改?郁闷啊!

其实,你大可不必如此烦恼,使用Wordpress的子主题功能,一切问题都可以迎刃而解!

WordPress 子主题简介

WordPress子主题也是一个主题,它继承了另一个主题——父主题——的功能,并允许你对父主题的功能进行修改,或者添加新功能。

创建一个子主题是很简单的。创建一个目录,将格式编写正确的 style.css 文件放进去,一个子主题就做成了!

只需要对 HTML 和CSS 具有基本的了解,您就可以通过创建一个非常基本的子主题 来对一个父主题的样式和布局进行修改和扩展,而不需要对父主题的文件作任何修改。

通过这样的方式,当父主题被更新的时候,您所做的修改就可以保存下来。

因为这个原因,我们强烈推荐您使用子主题的方式来对主题进行修改。

如果您对PHP, WordPress Templates,和 WordPress Plugin API有个基本的理解,理论上来讲,您可以使用子主题对父主题的每一个方面进行扩展,而不需要对父主题的文件进行任何修改。

本文将说明如何创建一个基本的子主题并解释您能用它来干什么。本文将使用 WordPress 3.0 的默认主题 Twenty Ten 作为父主题进行举例说明。

目录结构

子主题放在wp-content/themes目录下属于自己的目录里。下面的结构显示的就是子主题和它的父主题(Twenty Ten)在典型的WordPress目录结构中的位置:

  • public_html
    • wp-content
      • themes (主题存放的目录)
        • twentyten (示例中父主题Twenty Ten的目录)
        • twentyten-child (子主题存放的目录,可以任意命名)
          • style.css (子主题中不可或缺的文件,文件名必需为 style.css)

这个文件夹里面可以少至只包含一个style.css文件,也可以包含多至一个完整WordPress主题所拥有的文件:

  1. style.css (必需)
  2. functions.php (可选)
  3. Template files (可选)
  4. Other files (可选)

让我们看看它们是如何起作用的。

必需的style.css文件

style.css是一个子主题唯一必须的文件。它的头部提供的信息让WordPress辨认出子主题,并且重写父主题中的style.css文件

对于任何WordPress主题,头部信息必须位于文件的顶端,唯一的区别就是子主题中的Template:行是必须的,因为它让WordPress知道子主题的父主题是什么。

下面是一个style.css文件的头部信息的示例:

/*
Theme Name:     Twenty Ten Child
Theme URI:      http: //example.com/
Description:    Child theme for the Twenty Ten theme
Author:         Your name here
Author URI:     http: //example.com/about/
Template:       twentyten
Version:        0.1.0
*/

逐行的简单解释:

  • Theme Name. (必需) 子主题的名称
  • Theme URI. (可选) 子主题的主页。
  • Description. (可选) 子主题的描述。比如:我的第一个子主题,真棒!
  • Author URI. (可选) 作者主页。
  • Author. (optional) 作者的名字。
  • Template. (必需) 父主题的目录名,区别大小写。 注意: 当你更改子主题名字时,要先换成别的主题。
  • Version. (可选) 子主题的版本。比如:0.1,1.0,等。

*/ 这个关闭标记的后面部分,就会按照一个常规的样式表文件一样生效,你可以把你想对WordPress应用的样式规则都写在它的后面。

要注意的是,子主题的样式表会替换父主题的样式表而生效。(事实上WordPress根本就不会载入父主题的样式表。)所以,如果你想简单地改变父主题中的一些样式和结构——而不是从头开始制作新主题——你必须明确的导入父主题的样式表,然后对它进行修改。

下面的例子告诉你如何使用@import规则完成这个。

一个子主题的范例

这个例子中的父主题是Twenty Ten,我们喜欢这个主题的几乎每个部分,除了网站标题的颜色,因为我想把它从黑色的改成绿色的。使用子主题的话,只用完成以下三个简单的步骤:

  1. wp-content/themes目录下创建一个新目录,并将它命名为twentyten-child(或其他你喜欢的名称)。
  2. 将下面的代码保存在名为style.css的文件里,并将它放到新建的这个文件夹。
  3. 到WordPress的控制台>主题,然后激活你的新主题:Twenty Ten Child。
/*
Theme Name: Twenty Ten Child
Description: Child theme for the Twenty Ten theme
Author: Your name here
Template: twentyten
*/
 
@import url("../twentyten/style.css");
 
#site-title a {
    color: #009900;
}

下面一步步解释上面代码的作用:

  1. /* 开启子主题的头部信息。
  2. Theme Name: 子主题名称的声明。
  3. Description: 主题的描述(可选,也可被省略)。
  4. Author: 作者名字的声明(可选,也可被省略)。
  5. Template: 声明子主题的父主题,换言之,父主题所在的文件夹的名称,区分大小写。
  6. */子主题头部信息的关闭标记。
  7. @import规则将父主题的样式表调入
  8. #site-title a 定义网站标题的颜色(绿色),覆盖父主题中相同的样式规则。

注意 @import 规则

需要注意的是,@import 规则之前没有其他的CSS样式规则,如果你将其他的规则置于它之上,那么它将无效,并且父主题的样式表不会被导入。

使用 functions.php

不像style.css,子主题中的functions.php不会覆盖父主题中对应功能,而是将新的功能加入到父主题的functions.php中。(其实它会在父主题文件加载之前先载入。)

这样,子主题的functions.php提供了一个灵活稳定的方式来修改父主题的功能。如果你想在你的主题里加入一些PHP函数,最快的方式可能是打开functions.php文件然后加入进去。但那样并不灵活:下次你的主题升级更新了,你加入的新功能就会丢失掉。相反地,如果你使用子主题,将functions.php文件放进去,再将你想加入的功能写进这个文件里,那么这个功能同样会工作得很好,并且对于父主题以后的升级更新,子主题中加入的功能也不会受到影响。

functions.php文件的结构非常简单:将PHP起始标签置于顶部,关闭标签置于底部,它们之间就写上你自己的PHP函数。你可以写得很多,也可以写得很少,反正按你所需。下面的示例是一个基本的functions.php文件的写法,作用是将favicon链接加入到HTML页面的head元素里面。

<?php
	function favicon_link() {
		echo '<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" />' . "\n";
	}
	add_action('wp_head', 'favicon_link');
?>

给主题作者的提示。事实上子主题的functions.php首先加载意味着你的主题的用户功能可插入——即子主题是可替换的——通过有条件地进行声明。例如:

if (!function_exists('theme_special_nav')) {
    function theme_special_nav() {
        //  Do something.
    }
}

用这种方式,子主题可以替换父主题中的一个PHP函数,只需要简单地对它再次声明。

模板文件

模板文件 在子主题中的表现和style.css一样,它们会覆盖父主题中的相同文件。子主题可以覆盖任何父主题模板中的文件,只需要创建同名文件就行。(注意:index.php在WordPress3.0及以上版本才能被覆盖。)

同样,这项WordPress的功能允许你修改父主题的样式功能而不用去编辑父主题的文件,并且你的修改能让你在更新父主题后继续保留。

下面是一些使用模板文件的子主题的例子:

  • 增加一个父主题没有提供的模板(例如:网站地图页面的模板,或者一单栏页面,它们在页面编辑,模板选择里是可用的)
  • 增加一个比父模板更加具体的模板(见模板级别)。(例如:新加的tag.php模板用于按tag归档的文章来代替父主题中通常的archive.php模板。)
  • 替换父主题中的一个模板.(例:使用你自己的home.php来覆盖父主题中的home.php

其他文件

除 了style.css,functions.php,index.php和home.php,子主题可以使用任何正式主题使用的类型的文件,只要文件被正确链接。打个比方,你可以使用在样式表里或者Javascript文件里链接的图标、图片,或者从functions.php文件中调用出来的额外PHP文件。

引用链接


使用WordPress的子主题功能修改你的WordPress主题

在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引发内存泄漏
 Java 代码部分
 class TestLocalReference { 
 private native void nativeMethod(int i); 
 public static void main(String args[]) { 
         TestLocalReference c = new TestLocalReference(); 
         //call the jni native method 
         c.nativeMethod(1000000); 
 }  
 static { 
 //load the jni library 
 System.loadLibrary("StaticMethodCall"); 
 } 
 } 

 JNI代码,nativeMethod(int i)的C语言实现
 #include<stdio.h> 
 #include<jni.h> 
 #include"TestLocalReference.h"
 JNIEXPORT void JNICALL Java_TestLocalReference_nativeMethod 
 (JNIEnv * env, jobject obj, jint count) 
 { 
 jint i = 0; 
 jstring str; 

 for(; i<count; i++) 
         str = (*env)->NewStringUTF(env, "0"); 
 } 
运行结果
 JVMCI161: FATAL ERROR in native method: Out of memory when expanding 
 local ref table beyond capacity 
 at TestLocalReference.nativeMethod(Native Method) 
 at TestLocalReference.main(TestLocalReference.java:9)

运行结果证明,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引发内存泄漏
 Java 代码部分参考实例 1,未做任何修改。

 JNI 代码,nativeMethod(int i) 的 C 语言实现
 #include<stdio.h> 
 #include<jni.h> 
 #include"TestLocalReference.h"
 jstring CreateStringUTF(JNIEnv * env) 
 { 
 return (*env)->NewStringUTF(env, "0"); 
 } 
 JNIEXPORT void JNICALL Java_TestLocalReference_nativeMethod 
 (JNIEnv * env, jobject obj, jint count) 
 { 
 jint i = 0; 
 for(; i<count; i++) 
 { 
         str = CreateStringUTF(env); 
 } 
 } 
运行结果
 JVMCI161: FATAL ERROR in native method: Out of memory when expanding local ref 
 table beyond  capacity 
 at TestLocalReference.nativeMethod(Native Method) 
 at TestLocalReference.main(TestLocalReference.java:9)

运行结果证明,实例 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.)

 class MyPeer {
 public:
   MyPeer(jstring s) {
     str_ = s; // Error: stashing a reference without ensuring it’s global.
   }
   jstring str_;
 };

 static jlong MyClass_newPeer(JNIEnv* env, jclass) {
   jstring local_ref = env->NewStringUTF("hello, world!");
   MyPeer* peer = new MyPeer(local_ref);
   return static_cast<jlong>(reinterpret_cast<uintptr_t>(peer));
   // Error: local_ref is no longer valid when we return, but we've stored it in 'peer'.
 }

 static void MyClass_printString(JNIEnv* env, jclass, jlong peerAddress) {
   MyPeer* peer = reinterpret_cast<MyPeer*>(static_cast<uintptr_t>(peerAddress));
   // Error: peer->str_ is invalid!
   ScopedUtfChars s(env, peer->str_);
   std::cout << s.c_str() << std::endl;
 }

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:

 class MyPeer {
 public:
   MyPeer(JNIEnv* env, jstring s) {
     this->s = env->NewGlobalRef(s);
   }
   ~MyPeer() {
     assert(s == NULL);
   }
   void destroy(JNIEnv* env) {
     env->DeleteGlobalRef(s);
     s = NULL;
   }
   jstring s;
 };

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:

    JNI ERROR (app bug): accessed stale local reference 0x5900021 (index 8 in a table of size 8)
    JNI WARNING: jstring is an invalid local reference (0x5900021)
                 in LMyClass;.printString:(J)V (GetStringUTFChars)
    "main" prio=5 tid=1 RUNNABLE
      | group="main" sCount=0 dsCount=0 obj=0xf5e96410 self=0x8215888
      | sysTid=11044 nice=0 sched=0/0 cgrp=[n/a] handle=-152574256
      | schedstat=( 156038824 600810 47 ) utm=14 stm=2 core=0
      at MyClass.printString(Native Method)
      at MyClass.main(MyClass.java:13)

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

 JNI WARNING: threadid=8 using env from threadid=1
                 in LMyClass;.printString:(J)V (GetStringUTFChars)
    "Thread-10" prio=5 tid=8 NATIVE
      | group="main" sCount=0 dsCount=0 obj=0xf5f77d60 self=0x9f8f248
      | sysTid=22299 nice=0 sched=0/0 cgrp=[n/a] handle=-256476304
      | schedstat=( 153358572 709218 48 ) utm=12 stm=4 core=8
      at MyClass.printString(Native Method)
      at MyClass$1.run(MyClass.java:15)

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:

 static jclass gMyClass;
 static jclass gSomeClass;

 static void MyClass_nativeInit(JNIEnv* env, jclass myClass) {
   // ‘myClass’ (and any other non-primitive arguments) are only local references.
   gMyClass = env->NewGlobalRef(myClass);

   // FindClass only returns local references.
   jclass someClass = env->FindClass("SomeClass");
   if (someClass == NULL) {
     return; // FindClass already threw an exception such as NoClassDefFoundError.
   }
   gSomeClass = env->NewGlobalRef(someClass);
 }

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

    JNI ERROR (app bug): attempt to use stale local reference 0x4200001d (should be 0x4210001d)
    JNI WARNING: 0x4200001d is not a valid JNI reference
                 in LMyClass;.useStashedClass:()V (IsSameObject)

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:

 static jobjectArray MyClass_returnArray(JNIEnv* env, jclass) {
   env->PushLocalFrame(256);
   jobjectArray array = env->NewObjectArray(128, gMyClass, NULL);
   for (int i = 0; i < 128; ++i) {
       env->SetObjectArrayElement(array, i, newMyClass(i));
   }
   env->PopLocalFrame(NULL); // Error: should pass 'array'.
   return array; // Error: array is no longer valid.
 }

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:

  JNI ERROR (app bug): accessed stale local reference 0x2d00025 (index 9 in a table of size 8)
    JNI WARNING: invalid reference returned from native code
                 in LMyClass;.returnArray:()[Ljava/lang/Object;

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局部引用的变化

浏览器工作原理

看到一篇很好的文章,浏览器的工作原理,http://www.html5rocks.com/zh/tutorials/internals/howbrowserswork/

看一遍,把要点梳理一下。

1.浏览器的结构

1)UI  主界面、地址栏、前进后退等

2)Browser engine,adapter层。

3)render engine,如果adapter层发来html的请求,render会解析html和css,并将结果最终渲染到屏幕上。

4)network,底层网络模块,与平台无关,负责所有网络接口。

5)JavaScript解释器,负责解析和执行JS

6)UI后端,绘制基本的窗口小控件,和平台无关,在底层使用操作系统的绘制方法。

7)数据存储,如cookie和indexdb等

layers

Render Engine,渲染引擎

渲染引擎是web的核心,因为web中一般不做复杂的计算等操作。页面需要及时响应用户操作,流畅的渲染。那么来看一下渲染的基本流程

flow

1.解析HTML,生成DOM-TREE,解析CSS,生成CSSOM,合成RenderTree,就是包含一堆矩形的树,树结构代表了显示顺序。

2.layout,计算位置

3.paint

下图是webkit的渲染过程

webkitflow

解析过程

上面的流程中,HTML Parser CSS Parser都涉及到了一个解析的过程,什么是解析?

解析就是把文档转化成代码能理解的结构,生成结果被称为解析树或语法树。

解析基于确定的语法规则,即与上下文无关的语法。

解析可以分为两个过程:词法分析和语法分析。

解析把两个过程交给两个组件,词法分析器和解析器。

词法分析器负责把输入内容分成一个个标记。

解析器负责把标记构建成解析树。解析器的解析是一个迭代过程,即向词法分析器请求一个新标记,如果匹配语法规则,则把对应该标记的节点添加到解析树中,如果不匹配,则把这个标记缓存,继续请求标记,直到找到一个匹配的语法规则。

解析树往往还不是最终产品,这个时候需要使用编译器把解析树转成机器代码。

image013

一般的解析可以去看龙书,Html语言不适用于常规解析器,因为HTML不是一种上下文无关的语言,这源于HTML有一定的容错能力。CSS和JavaScript可以。

HTML有自定义的解析器,采用DTD格式定义。

解析算法分为两个步骤,标记化和构建树:

标记化算法使用状态机驱动,该算法比较复杂,通过一个例子叙述:

<html>
  <body>
    Hello world
  </body>
</html>

1.初始状态为 数据状态,

2.遇到<时,更改为标记打开状态,

3.之后读取一个a-z字符,进入标记名状态

4.直到遇到>,进入数据状态

5.数据状态下去读Hello world,直到<,进入标记打开状态

6.标记打开状态下遇到/号,创建 end tag token并进入标记名状态

7.读取a-z字符直到>,回到数据状态。

解析器创建时,会创建Document对象。在解析器工作的时候,已Document为根节点的DOM树不断更新,解析器生成的元素不仅仅会加入DOM树,还会放到堆栈中去,堆栈用来纠正嵌套错误和未关闭的标记。

树构建过程

树构建阶段的输入是一个来自标记化阶段的标记序列。第一个模式是“initial mode”。接收 HTML 标记后转为“before html”模式,并在这个模式下重新处理此标记。这样会创建一个 HTMLHtmlElement 元素,并将其附加到 Document 根对象上。

然后状态将改为“before head”。此时我们接收“body”标记。即使我们的示例中没有“head”标记,系统也会隐式创建一个 HTMLHeadElement,并将其添加到树中。

现在我们进入了“in head”模式,然后转入“after head”模式。系统对 body 标记进行重新处理,创建并插入 HTMLBodyElement,同时模式转变为“in body”

现在,接收由“Hello world”字符串生成的一系列字符标记。接收第一个字符时会创建并插入“Text”节点,而其他字符也将附加到该节点。

接收 body 结束标记会触发“after body”模式。现在我们将接收 HTML 结束标记,然后进入“after after body”模式。接收到文件结束标记后,解析过程就此结束。

image022

树构建过程

解析过程结束后,浏览器将文档注为交互状态,开始加载 defferd模式下的脚本。然后文档状态成为完成,随之触发加载事件。

容错

浏览器底层有大量的代码来对诸如 无效tag,结束符错误等容错。

来看看webkit的容错说明

解析器对标记化输入内容进行解析,以构建文档树。如果文档的格式正确,就直接进行解析。

遗憾的是,我们不得不处理很多格式错误的 HTML 文档,所以解析器必须具备一定的容错性。

我们至少要能够处理以下错误情况:

  1. 明显不能在某些外部标记中添加的元素。在此情况下,我们应该关闭所有标记,直到出现禁止添加的元素,然后再加入该元素。
  2. 我们不能直接添加的元素。这很可能是网页作者忘记添加了其中的一些标记(或者其中的标记是可选的)。这些标签可能包括:HTML HEAD BODY TBODY TR TD LI(还有遗漏的吗?)。
  3. 向 inline 元素内添加 block 元素。关闭所有 inline 元素,直到出现下一个较高级的 block 元素。
  4. 如果这样仍然无效,可关闭所有元素,直到可以添加元素为止,或者忽略该标记。
  5. 示例网站 www.liceo.edu.mx 嵌套了约 1500 个标记,全都来自一堆 <b> 标记。我们只允许最多 20 层同类型标记的嵌套,如果再嵌套更多,就会全部忽略。
  6. 支持格式非常糟糕的 HTML 代码。我们从不关闭 body 标记,因为一些愚蠢的网页会在实际文档结束之前就关闭。我们通过调用 end() 来执行关闭操作。

CSS解析是遵循一般的解析规则的,这里不赘述。

脚本和文档处理的顺序

当遇到一个script标签时,会立即停止文档的解析,进行js解析。

如果script标签带有defer标记,则会在文档解析完成后再解析。

h5增加了一个标记,将script标记为异步,则会开线程去解析它。

预解析

这是一项优化。在执行脚本的时候,其他线程回去解析剩下的文档部分,找出并加载需要下载的网络资源。预解析器不会动DOM树,只是寻找需要下载的资源。

样式表

理论上CSS和HTML不是一个解析器,但有些JS会请求CSS,所以浏览器在解析CSS的时候会禁止有交互的脚本。

Render-Tree

在DOM-Tree构建的时候,同时也在生成一个Render-Tree。Render-Tree是DOM-Tree的子集,它不包含那些display:none的元素,以及head等。

一般情况下DOM-Tree和Render-Tree的位置是对应的,但不包括absolute和float等非文档流内容,它们位于其他位置,原位置放一个占位符。

样式计算

样式计算是个复杂而庞大的工程,如 匹配规则为 div div div div,这样的匹配会很耗时,而且找了很久可能找到一个三层的。

浏览器为了样式计算进行了很多工作

共享样式数据
规则树
结构划分
使用规则树计算样式上下文
对规则进行处理以简化匹配
以正确的层叠顺序应用规则
样式表层叠顺序
特异性

特异性这里详细记一下

某个样式的属性声明可能会出现在多个样式表中,同一个样式表中也可能出现多次,那么他们的重要性为(优先级从低到高):

1.浏览器声明

2.用户普通声明

3.作者普通声明

4.作者重要声明

5.用户重要声明

同样顺序的声明,按照特异性排序

特异性是 a-b-c-d

a:声明来自style,标记为1,否则为0

b:选择器中ID属性的个数

c:选择器中class属性和伪类的个数

d:选择器中元素名称和伪类的个数

规则排序

渐进式处理

这里不详细叙述。

布局-layout

layout是一个比较重的环节,不仅仅是layout消耗时间,而是layout后一定会引发paint。

HTML采用流式布局,意味着可以遍历一次就计算出来所有位置,从左到右,从上到下。有些特殊的如 表格需要多次遍历。

每一个节点都有一个layout/reflow方法,是一个递归过程。

Dirty位系统

为了避免细小改动都要进程整体布局,浏览器采用了Dirty位的设计。如果某个节点发生了改变,则将其与子代标注为dirty。还有一种标记,children are dirty,表示节点至少有一个子代需要重新布局。

布局分全局和增量两种

全局指必须重新布局,增量是只布局dirty节点(可能还需要布局其他元素)。全局布局是同步的,增量布局一般是异步的。

布局优化,如果只是位置改变或者缩放等,可以直接从缓存取原大小。某些子树修改,不必全局重新layout,如input,不会每输入一次就来一次全局更新。

Paint

paint的内容比较少,只是调用节点的paint方法。paint也有全局绘制和增量绘制两种。

优化

webkit在paint时会缓存之前的矩形,只绘制新旧矩形的差异部分。而当元素有了变化,浏览器会尽量做最小的改动。

 

线程

Render engin是单线程。

loop

主线程是一个loop,事件循环。

 

CSS模型

可视化模型,指画布

框模型,由框大小,padding,margin,border组成,display指定

image046

position

普通  浮动 绝对

display:block  普通的矩形,拥有自己的区域

display:inline 没有自己的区域,位于容器block中

image061

block是垂直的,inline是水平的

分层

z-index表示了框的第三个维度

 

adb shell下向应用发送GC命令

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

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

private void gc() {
        String pid = String.valueOf(ProcessUtils
                .getProcessPID(AUTManager.pkn.toString()));

        if (!pid.equals("-1")) {
            boolean isSucess = true;
            ProcessBuilder pb = null;

            String cmd = "kill -10 " + pid;
            pb = new ProcessBuilder("su", "-c", cmd);

            Process exec = null;

            pb.redirectErrorStream(true);
            try {
                exec = pb.start();

                InputStream is = exec.getInputStream();
                BufferedReader reader = new BufferedReader(
                        new InputStreamReader(is));

                while ((reader.readLine()) != null) {
                    isSucess = false;
                }
            } catch (Exception e) {
                e.printStackTrace();
                isSucess = false;
            }
            // 至此命令算是执行成功
            if (isSucess)
            {
                handler.sendEmptyMessage(5);
            }

        } else {
            Log.d("gc error", "pid not found!");
        }
    }

    private void dumpHeap() {
        String pid = String.valueOf(ProcessUtils
                .getProcessPID(AUTManager.pkn.toString()));

        if (!pid.equals("-1")) {
            boolean isSucess = true;
            ProcessBuilder pb = null;

            String sFolder = Env.S_ROOT_DUMP_FOLDER + AUTManager.pkn.toString() + "/";
            File folder = new File(sFolder);
            if (!folder.exists())
            {
                folder.mkdirs();
            }

            String cmd = "am dumpheap " + pid + " "// 命令
                    + Env.S_ROOT_DUMP_FOLDER + AUTManager.pkn.toString() + "/"// 输出路径
                    + "dump_" + pid + "_" + GTUtils.getSaveDate() + ".hprof"; // 输出文件名
            pb = new ProcessBuilder("su", "-c", cmd);

            Process exec = null;

            pb.redirectErrorStream(true);
            try {
                exec = pb.start();

                InputStream is = exec.getInputStream();
                BufferedReader reader = new BufferedReader(
                        new InputStreamReader(is));

                while ((reader.readLine()) != null) {
                    isSucess = false;
                }
            } catch (Exception e) {
                e.printStackTrace();
                isSucess = false;
            }
            // 至此命令算是执行成功
            if (isSucess)
            {
                handler.sendEmptyMessage(6);
            }

        } else {
            Log.d("dump error", "pid not found!");
        }
    }

相同道理,我们只要直接在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

pcre *pcre_compile(const char *pattern, int options,
            const char **errptr, int *erroffset,
            const unsigned char *tableptr);
功能:编译指定的正则表达式
参数:pattern, 输入参数,将要被编译的字符串形式的正则表达式
      options, 输入参数,用来指定编译时的一些选项
      errptr, 输出参数,用来输出错误信息
      erroffset, 输出参数,pattern中出错位置的偏移量
      tableptr, 输入参数,用来指定字符表,一般情况用NULL, 使用缺省的字符表
返回值:被编译好的正则表达式的pcre内部表示结构

(2). pcre_exec

int pcre_exec(const pcre *code, const pcre_extra *extra,
            const char *subject, int length, int startoffset,
            int options, int *ovector, int ovecsize);
功能:用来检查某个字符串是否与指定的正则表达式匹配
参数: code, 输入参数,用pcre_compile编译好的正则表达结构的指针
      extra, 输入参数,用来向pcre_exec传一些额外的数据信息的结构的指针
      subject, 输入参数,要被用来匹配的字符串
      length, 输入参数, 要被用来匹配的字符串的指针
      startoffset, 输入参数,用来指定subject从什么位置开始被匹配的偏移量
      options, 输入参数, 用来指定匹配过程中的一些选项
      ovector, 输出参数,用来返回匹配位置偏移量的数组
      ovecsize, 输入参数, 用来返回匹配位置偏移量的数组的最大大小
返回值:匹配成功返回非负数,匹配返回负数

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使用举例
下面是例程:

#include <pcre.h>
#include <stdio.h>
#include <string.h>
 
int main(int argc, char ** argv)
{
    if (argc != 3)
    {   
        printf("Usage: %s pattern text\n", argv[0]);
        return 1;
    }   
 
    const char * pPattern = argv[1];
    const char * pText = argv[2];
    const char * pErrMsg = NULL;
    pcre * pPcre = NULL;
    int nOffset = -1; 
 
    if (NULL == (pPcre = pcre_compile(pPattern, 0, &pErrMsg, &nOffset, NULL)))
    {   
        printf("ErrMsg=%s, Offset=%d\n", pErrMsg, nOffset);
        return 1;
    }   
    else
    {   
        if (pcre_exec(pPcre, NULL, pText, strlen(pText), 0, 0, NULL, 0) < 0)
        {   
            printf("%s doesn't match %s\n", pText, pPattern);
        }   
        else
        {   
            printf("%s matches %s\n", pText, pPattern);
        }
    }
}

下面是运行结果:

$ g++ -lpcre TestPcre.cpp -o pcre

$ ./pcre "http:\/\/.*\.qq\.com" "http://www.qq.com"
http://www.qq.com matches http:\/\/.*\.qq\.com
$ ./pcre "http:\/\/.*\.qq\.com" "http://www.qqq.com"
http://www.qqq.com doesn't match http:\/\/.*\.qq\.com

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

#include <iostream>
#include <pcrecpp.h>
 
int main(int argc, char ** argv)
{
    if (argc != 3)
    {   
        std::cerr < < "Usage: " << argv[0] << " pattern text\n";
        return 1;
    }   
 
    pcrecpp::RE oPattern(argv[1]);
    if (oPattern.FullMatch(argv[2]))
    {   
        std::cout << argv[2] << " fully matches " << argv[1] << "\n";
    }   
    else if (oPattern.PartialMatch(argv[2]))
    {   
        std::cout << argv[2] << " partially matches " << argv[1] << "\n";
    }   
    else
    {   
        std::cout << argv[2] << " dose not match " << argv[1] << "\n";
    }   
}

下面是运行结果:

$ g++ TestPcreCpp.cpp -lpcrecpp -o pcrecpp

$ ./pcrecpp 
Usage: ./pcrecpp pattern text
$ ./pcrecpp "http:\/\/.*\.qq\.com" "http://www.qq.com"
http://www.qq.com fully matches http:\/\/.*\.qq\.com
$ ./pcrecpp "http:\/\/.*\.qq\.com" "http://www.qq.comiii"
http://www.qq.comiii partially matches http:\/\/.*\.qq\.com
$ ./pcrecpp "http:\/\/.*\.qq\.com" "http://www.qqq.comiii"
http://www.qqq.comiii dose not match http:\/\/.*\.qq\.com

参考链接


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

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

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

<script src="LinkGame/js/LinkGame.js"></script>

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

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

<input id="PageContext" type="hidden" value="${pageContext.request.contextPath}" />

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

$(document).ready(function() {
    var pageContext = g("PageContext").value;
});