ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
图3.2展示了一个程序员如何在字符串相关的函数中做选择。 ![](https://box.kancloud.cn/2016-02-24_56cd74cb3b632.png) 图3.2 在JNI字符串函数中做选择。 如果你想让你的代码发布在JDK1.1的目标平台上,除了Get/ReleaseStringChars和Get/ReleaseStringUTFChars你毫无选择。 如果你是在Java 2 SDK release 1.2及其以上版本上编程,你想讲字符串内容拷贝到事先分配好的内存中区,你可以使用GetStringRegion或者GetStringUTFRegion. 对于很小的固定大小的字符串,Get/SetStringRegion和Get/SetStringUTFRegion往往更优因为你可以在C堆栈上开辟空间,这样的代价相对来说小得多了,拷贝一个小字符串的代价是微乎其微的。 使用Get/SetStringRegion和Get/SetStringUTFRegion的一个好处是它们不必开辟内存空间,因此也就不会抛出out-of-memory相关的异常。如果你可以确定下标越界这种情况不会发生你就永远不需要进行检查。 使用Get/SetStringRegion和Get/SetStringUTFRegion的另一个好处是你可以指定要拷贝的字符串在原始字符串中的其实位置以及要拷贝的字符个数。这个函数适用于那些只需要访问一个比较长的字符串的部分子字符串的场景。 GetStringCritical必须被小心的使用,必须保证在持有GetStringCritical返回的指针的过程中,原生代码没有在虚拟机中开辟任何新的空间,而且不能执行任何阻塞式操作,否则有可能会引起死锁。 以下是使用GetStringCritical的错误示例,以下函数使用GetStringCritical获取了一个字符串,然后调用fprint函数将这个字符串输出到指定的文件描述符fd。 ~~~ /* This is not safe! */ const char* c_str = (*env)->GetStringCritical(env, j_str, 0); i(!c_str) { /* error handling */ } fprintf(fd, "%s\n", c_str); (*env)->ReleaseStringCritical(env, j_str, c_str); ~~~ 以上代码的问题在于当当前线程的垃圾回收被禁止的时候, 写入一个文件并不总是安全的。举个例子,假设另一个线程T正在等待从fd 对应的文件读取内容。让我们跟进一步假设文件系统的缓冲机制的实现是这样的:fprintf调用会一直等待线程T完成从fd读取所有数据。 我们创建了一个可能产生死锁的场景:如果线程T不能够开辟足够的空间来缓存从fd读取的内容,它就必须发起一次垃圾回收请求。但是垃圾回收请求又必须等到当前线程执行ReleaseStringCritical函数,而按照我们以上代码,这个操作在frpintf返回之前是不可能被调用的,这样的话fprintf函数就会一直等待。 以下代码,与上边的示例代码相似,但是他是不会产生任何死锁的: /*This code segment is OK。*/ const char *c_str = (*env)->GetStringCritical(env, j_str, 0); if(!c_str) { /* error handling */ } DrawString(c_str); (*env)->ReleaseStringCritical(env, j_str, c_str); DrawString是一个系统API调用, 它直接将字符串输出到屏幕。除非屏幕显示器的驱动也是跟当前代码在同一个Java虚拟机中运行的Java程序,DrawString函数是永远不会阻塞或者无限等待垃圾回收的。 总的来说,在使用Get/ReleaseStringCritical函数调用的时候,你㤇尽可能的多考虑你的代码是否会发生阻塞场景。