VisualVM java 分析 - 自我执行?

VisualVM java profiling - self time execution?

我有以下Java方法:

static Board board;
static int[][] POSSIBLE_PLAYS; // [262143][0 - 81]

public static void playSingleBoard() {
    int subBoard = board.subBoards[board.boardIndex];
    int randomMoveId = generateRandomInt(POSSIBLE_PLAYS[subBoard].length);
    board.play(board.boardIndex, POSSIBLE_PLAYS[subBoard][randomMoveId]);
}

访问的数组在运行时不会改变。该方法总是由同一个线程调用。 board.boardIndex可能会从0变为8,一共有9个子板。

在 VisualVM 中,我最终执行了 2 228 212 次方法,其中 (Total Time CPU) :

Self Time 27.9%
Board.play(int, int) 24.6%
MainClass.generateRnadomInt(int) 8.7%

我想知道的是那些 27.9% 的自我执行 (999ms / 2189ms) 是从哪里来的。 我首先认为分配 2 int 可能会减慢该方法的速度,所以我尝试了以下操作:

public static void playSingleBoard() {
    board.play(
     board.boardIndex,
     POSSIBLE_PLAYS[board.subBoards[board.boardIndex]]
     [generateRandomInt(POSSIBLE_PLAYS[board.subBoards[board.boardIndex]].length)]
    );
}

但结果相似,我不知道这个自执行时间是多少..是 GC 时间吗?内存访问?

我已尝试使用此处提到的 JVM 选项 => VisualVM - strange self time 没有。

首先,Visual VM(以及许多其他基于安全点的分析器)inherently misleading. Try using a profiler 不受安全点偏差的影响。例如。 async-profiler 不仅可以显示方法,还可以显示花费最多 CPU 时间的特定 lines/bytecodes。

其次,在您的示例中,playSingleBoard 可能确实需要相对较长的时间。即使没有分析器,我也可以看出这里最昂贵的操作是大量的数组访问。

RAM is the new disk。内存访问不是免费的,尤其是随机访问。特别是当数据集太大无法放入 CPU 缓存时。此外,Java 中的数组访问需要进行边界检查。此外,Java 中没有“真正的”二维数组,它们更像是数组的数组。
这意味着,像 POSSIBLE_PLAYS[subBoard][randomMoveId] 这样的表达式将导致至少 5 次内存读取和 2 次边界检查。并且每次出现 L3 缓存未命中(对于像您的情况这样的大型数组来说很可能),这将导致 ~50 ns latency - 否则时间足以执行一百次算术运算。