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 来说,实际上真正需要确保已分配的“临界区”对象不会移动。有以下几种实现:

  1. 一旦有临界区对象分配成功后”禁用GC“。这是最简单的策略,不影响 GC 的其他部分。缺点是必须无限期禁用 GC 直到用户释放,这可能会有问题。
  2. “固定对象”并在垃圾回收过程中绕过。缺点是如果收集器希望分配连续空间或者希望回收整个堆子空间,那么就很难实现。举例来说,在使用简单逐代回收算法情况下,如果将对象固定在年轻代里,回收完成后就不能“忽略”年轻代中剩下的内容。而且也不能从这里移动对象,因为这会破坏需要保持的对象。
  3. ”固定包含指定对象的子空间“。同样的,如果 GC 以 generation 为粒度进行回收,那么这种方法无效。但如果堆按照 region 划分,那么可以固定单个 region 并且只针对该 region 禁用 GC,皆大欢喜。

有人通过 JNI Critical 临时禁用 GC,但这只对第1种情况有效。而且每种收集器都采用这种简单化方法。

在网上查到了一篇翻译,参考

https://www.javazhiyin.com/41795.html

3.Tip:继续学习相关linux编程的知识。

4.share:https://thurstonzk2008.com/2019/11/19/spring-cloud-gateway%e6%a6%82%e5%bf%b5%e9%83%a8%e5%88%86%e5%86%85%e5%ae%b9%e6%80%bb%e7%bb%93/