1.Algorithm:https://leetcode-cn.com/problems/move-zeroes/
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
示例:
输入: [0,1,0,3,12]
输出: [1,3,12,0,0]
说明:
必须在原数组上操作,不能拷贝额外的数组。
尽量减少操作次数。
方法比较多,可以暴力,遇到0删除,把后面的元素前移。
双指针方法,1个指针用来遍历,第二个指针指向下一个非0的元素。遍历到非0元素,把值赋值给第二个指针指向的元素。
也可以采用交换的方法,遇到非0元素,与后面的为0的元素进行位置交换
参考代码如下:
public class MoveZeroes {
// leetcode 283
public void moveZeroes(int[] nums) {
int j = 0;
for (int i = 0; i < nums.length; ++i) {
if (nums[i] != 0) {
nums[j] = nums[i];
j++;
}
}
for (; j < nums.length; ++j) {
nums[j] = 0;
}
}
public void moveZeroes2(int[] nums) {
int j = 0;
for (int i = 0; i < nums.length; ++i) {
if (nums[i] != 0) {
nums[j] = nums[i];
if (i != j) {
nums[i] = 0;
}
j++;
}
}
}
}
2.Review:https://shipilev.net/jvm/anatomy-quarks/9-jni-critical-gclocker/
JNI临界区和GC锁
JNI `Get*Critical` 如何与 GC 配合?GC Locker 是什么?
熟悉 JNI 的人知道有两组读取数组内容的方法,包括 Get<PrimitiveType>Array*
:
void * GetPrimitiveArrayCritical(JNIEnv *env, jarray array, jboolean *isCopy);
void ReleasePrimitiveArrayCritical(JNIEnv *env, jarray array, void *carray, jint mode);
这两个函数的语义非常类似于 `Get/Release*ArrayElements` 函数。可能的情况 VM 会返回一个指向原始数据的指针,或者进行拷贝。但是,如何使用这些函数有很多限制。
这样做的好处显而易见:VM 返回指针可以提高性能,而不是对 Java 数组进行拷贝。显然这会有一些限制:
调用 `GetPrimitiveArrayCritical` 后,原生代码在调用 `ReleasePrimitiveArrayCritical` 前不应该长时间运行。两个函数之间的代码应视为“临界区”。在临界区内,原生代码不允许调用其他 JNI 函数;也不允许调用任何其他阻塞当前线程的系统调用,等待其他 Java 线程完成(例如,另一个正在执行写操作,当前线程对写入的 stream 执行读操作)。
即使 VM 本身不支持 pinning,这些限制也能让原生代码更有机会得到数组指针而非数组拷贝。例如,当原生代码通过 `GetPrimitiveArrayCritical` 取得数组指针时,VM 可能暂时禁用垃圾回收。
译注:CPU pinning,又称 processor affinity,指将进程和某个或者某几个 CPU 关联绑定,绑定后的进程只能在所关联的 CPU 上运行。本文中 pin object 指的是把对象或子空间固定在内存中某个区域。
从上面的介绍中似乎可以得到这样的信息:当进入临界区时 VM 会停止 GC。
对于 VM 来说,实际上真正需要确保已分配的“临界区”对象不会移动。有以下几种实现:
- 一旦有临界区对象分配成功后”禁用GC“。这是最简单的策略,不影响 GC 的其他部分。缺点是必须无限期禁用 GC 直到用户释放,这可能会有问题。
- “固定对象”并在垃圾回收过程中绕过。缺点是如果收集器希望分配连续空间或者希望回收整个堆子空间,那么就很难实现。举例来说,在使用简单逐代回收算法情况下,如果将对象固定在年轻代里,回收完成后就不能“忽略”年轻代中剩下的内容。而且也不能从这里移动对象,因为这会破坏需要保持的对象。
- ”固定包含指定对象的子空间“。同样的,如果 GC 以 generation 为粒度进行回收,那么这种方法无效。但如果堆按照 region 划分,那么可以固定单个 region 并且只针对该 region 禁用 GC,皆大欢喜。
有人通过 JNI Critical 临时禁用 GC,但这只对第1种情况有效。而且每种收集器都采用这种简单化方法。
在网上查到了一篇翻译,参考
https://www.javazhiyin.com/41795.html
3.Tip:继续学习相关linux编程的知识。