JNI使用的是改良的UTF-8格式的Strings。
以下文档来自官方:
Modified UTF-8 Strings
The JNI uses modified UTF-8 strings to represent various string types. Modified UTF-8 strings are the same as those used by the Java VM. Modified UTF-8 strings are encoded so that character sequences that contain only non-null ASCII characters can be represented using only one byte per character, but all Unicode characters can be represented.
翻译:
JNI使用的是改良的UTF-8格式的Strings来表示各种字符串类型。改良的UTF-8的strings和Java VM使用的是一样的。改良的UTF-8的strings编码,使得仅包含非空ASCII字符的字符序列能够按每字符占一个字节来表示,但所有的Unicode字符均可以被表示出来。
String Operations(String 操作)
1.NewString -- 创建String
jstring NewString(JNIEnv *env, const jchar *unicodeChars,jsize len);
Constructs a new java.lang.String
object from an array of Unicode characters.
通过Unicode字符的数组来创建一个新的String对象。备注:Unicode和UTF-8还是有区别的。
参数:
env:JNI 接口指针。
unicodeChars
:指向 Unicode 字符串的指针。
len
:Unicode 字符串的长度。
返回值:
Java 字符串对象。如果无法构造该字符串,则为 NULL
。
抛出:
OutOfMemoryError:如果系统内存不足。
2.GetStringLength -- 获取String的长度
jsize GetStringLength(JNIEnv *env, jstring string);
Returns the length (the count of Unicode characters) of a Java string.
返回 Java 字符串的长度(Unicode 字符数)。
参数:
env:JNI 接口指针。
string:Java 字符串对象。
返回值:
Java 字符串的长度。
3.GetStringChars -- 获取StringChars的指针
const jchar * GetStringChars(JNIEnv *env, jstring string,jboolean *isCopy);
Returns a pointer to the array of Unicode characters of the string. This pointer is valid until ReleaseStringchars()
is called.
If isCopy
is not NULL
, then *isCopy
is set to JNI_TRUE
if a copy is made; or it is set to JNI_FALSE
if no copy is made.
返回指向字符串的 Unicode 字符数组的指针。该指针在调用 ReleaseStringchars()
前一直有效。
如果 isCopy
非空,则在复制完成后将 *isCopy
设为 JNI_TRUE
。如果没有复制,则设为JNI_FALSE
。
参数:
env:JNI 接口指针。
string:Java 字符串对象。
isCopy
:指向布尔值的指针。
返回值:
指向 Unicode 字符串的指针,如果操作失败,则返回NULL
。
4.ReleaseStringChars -- 释放StringChars
void ReleaseStringChars(JNIEnv *env, jstring string,const jchar *chars);
Informs the VM that the native code no longer needs access to chars
. The chars
argument is a pointer obtained from string
using GetStringChars()
.
通知虚拟机平台相关代码无需再访问 chars
。参数 chars
是一个指针,可通过 GetStringChars()
从 string
获得。
参数:
env:JNI 接口指针。
string:Java 字符串对象。
chars
:指向 Unicode 字符串的指针。
UTF-8
5.NewStringUTF -- 创建UTF的String
jstring NewStringUTF(JNIEnv *env, const char *bytes);
Constructs a new java.lang.String
object from an array of characters in modified UTF-8 encoding.
通过改良的 UTF-8 字符数组构造新 java.lang.String
对象。
参数:
env:JNI 接口指针。如果无法构造该字符串,则为 NULL
。
bytes
:指向 UTF-8 字符串的指针。
返回值:
Java 字符串对象。如果无法构造该字符串,则为 NULL
。
抛出:
OutOfMemoryError:如果系统内存不足。
6.GetStringUTFLength -- 获取UTF的String的长度
jsize GetStringUTFLength(JNIEnv *env, jstring string);
Returns the length in bytes of the modified UTF-8 representation of a string.
以字节为单位返回字符串的 UTF-8 长度。
参数:
env:JNI 接口指针。
string:Java 字符串对象。
返回值:
返回字符串的 UTF-8 长度。
7.GetStringUTFChars -- 获取StringUTFChars的指针
const char * GetStringUTFChars(JNIEnv *env, jstring string,jboolean *isCopy);
Returns a pointer to an array of bytes representing the string in modified UTF-8 encoding. This array is valid until it is released by ReleaseStringUTFChars()
.
If isCopy
is not NULL
, then *isCopy
is set to JNI_TRUE
if a copy is made; or it is set to JNI_FALSE
if no copy is made.
返回指向字符串的 UTF-8 字符数组的指针。该数组在被ReleaseStringUTFChars()
释放前将一直有效。
如果 isCopy
不是 NULL
,*isCopy
在复制完成后即被设为 JNI_TRUE
。如果未复制,则设为 JNI_FALSE
。
参数:
env:JNI 接口指针。
string:Java 字符串对象。
isCopy
:指向布尔值的指针。
返回值:
指向 UTF-8 字符串的指针。如果操作失败,则为 NULL
。
8.ReleaseStringUTFChars -- 释放StringUTFChars
void ReleaseStringUTFChars(JNIEnv *env, jstring string,const char *utf);
Informs the VM that the native code no longer needs access to utf
. The utf
argument is a pointer derived from string
using GetStringUTFChars()
.
通知虚拟机平台相关代码无需再访问 utf
。utf
参数是一个指针,可利用 GetStringUTFChars()
从 string
获得。
参数:
env:JNI 接口指针。
string:Java 字符串对象。
utf
:指向 UTF-8 字符串的指针。
Note
In JDK/JRE 1.1, programmers can get primitive array elements in a user-supplied buffer. As of JDK/JRE 1.2 additional set of functions are provided allowing native code to obtain characters in Unicode (UTF-16) or modified UTF-8 encoding in a user-supplied buffer. See the functions below.
注意:
在JDK/JRE 1.1,程序员可以在用户提供的缓冲区获取基本类型数组元素。从JDK/JRE1.2之后,提供了额外的方法,这些方法允许在用户提供的缓冲区获取Unicode字符(UTF-16编码)或者是UTF-8的字符。这些方法详见如下:
9.GetStringUTFRegion
void GetStringRegion(JNIEnv *env, jstring str, jsize start, jsize len, jchar *buf);
Copies len
number of Unicode characters beginning at offset start
to the given buffer buf
.
Throws StringIndexOutOfBoundsException
on index overflow.
在str(Unicode字符)从start位置开始截取len长度放置到buf中。
抛出StringIndexOutOfBoundsException异常。
10.GetStringUTFRegion
void GetStringUTFRegion(JNIEnv *env, jstring str, jsize start, jsize len, char *buf);
Translates len
number of Unicode characters beginning at offset start
into modified UTF-8 encoding and place the result in the given buffer buf
.
Throws StringIndexOutOfBoundsException
on index overflow.
将str(Unicode字符)从start位置开始截取len长度转换为改良的UTF-8编码并将结果放置到buf中。
抛出StringIndexOutOfBoundsException异常。
11.GetStringCritical / ReleaseStringCritical
const jchar * GetStringCritical(JNIEnv *env, jstring string, jboolean *isCopy);
void ReleaseStringCritical(JNIEnv *env, jstring string, const jchar *carray);
The semantics of these two functions are similar to the existing Get/ReleaseStringChars
functions. If possible, the VM returns a pointer to string elements; otherwise, a copy is made. However, there are significant restrictions on how these functions can be used. In a code segment enclosed by Get/ReleaseStringCritical
calls, the native code must not issue arbitrary JNI calls, or cause the current thread to block.
The restrictions on Get/ReleaseStringCritical
are similar to those on Get/ReleasePrimitiveArrayCritical
.
这两个函数的语义是类似于现有的 Get/ReleaseStringChars 功能。如果可能的话,虚拟机返回一个指向字符串元素的指针;否则,将返回一个复制的副本。然而使用这些方法是有值得注意的限制的。In a code segment enclosed by
Get/ReleaseStringCritical
calls, the native code must not issue arbitrary JNI calls, or cause the current thread to block.
备注:
为了提高JVM返回字符串直接指针的可能性,JDK1.2中引入了一对新函数,Get/ReleaseStringCritical。表面上,它们和Get/ReleaseStringChars函数差不多,但实际上这两个函数在使用有很大的限制。
使用这两个函数时,你必须两个函数中间的代码是运行在"critical region"(临界区)的,即,这两个函数中间的本地代码不能调用任何会让线程阻塞或等待JVM中的其它线程的本地函数或JNI函数。
有了这些限制, JVM就可以在本地方法持有一个从GetStringCritical得到的字符串的直接指针时禁止GC。当GC被禁止时,任何线程如果触发GC的话,都会被阻塞。而Get/ReleaseStringCritical这两个函数中间的任何本地代码都不可以执行会导致阻塞的调用或者为新对象在JVM中分配内存。否则,JVM有可能死锁,想象一下这样的场景中:
1、 只有当前线程触发的GC完成阻塞并释放GC时,由其它线程触发的GC才可能由阻塞中释放出来继续运行。
2、 在这个过程中,当前线程会一直阻塞。因为任何阻塞性调用都需要获取一个正被其它线程持有的锁,而其它线程正等待GC。
Get/ReleaseStringCritical的交迭调用是安全的,这种情况下,它们的使用必须有严格的顺序限制。而且,我们一定要记住检查是否因为内存溢出而导致它的返回值是NULL。因为JVM在执行GetStringCritical这个函数时,仍有发生数据复制的可能性,尤其是当JVM内部存储的数组不连续时,为了返回一个指向连续内存空间的指针,JVM必须复制所有数据。
总之,为了避免死锁,在Get/ReleaseStringCritical之间不要调用任何JNI函数。Get/ReleaseStringCritical和 Get/ReleasePrimitiveArrayCritical这两个函数是可以的。
参考资料: