TL; DR版本,对于那些不想要背景的人,是以下具体问题:
为什么Java没有真正的多维数组的实现?有坚实的技术原因吗?我在这里错过了什么?
Java在语法级别具有多维数组,可以声明
int[][] arr = new int[10][10];
Run Code Online (Sandbox Code Playgroud)
但似乎这真的不是人们所期望的.它不是让JVM分配足够大的连续RAM块来存储100 int秒,而是作为ints 的数组阵列出现:所以每个层都是一个连续的RAM块,但整体而言并非如此.arr[i][j]因此访问速度相当慢:JVM必须这样做
int[]存储的arr[i];int存储的arr[i][j].这涉及查询对象从一层到另一层,这是相当昂贵的.
在一个层面上,不难看出为什么这不能被优化为简单的扩展和添加查找,即使它们都被分配在一个固定块中.问题是arr[3]它本身就是一个引用,它可以被改变.因此,尽管数组具有固定大小,但我们可以轻松编写
arr[3] = new int[11];
Run Code Online (Sandbox Code Playgroud)
现在,由于这一层已经成长,因此缩放和加载是固定的.您需要在运行时知道是否所有内容仍然与以前相同.此外,当然,这将被分配到RAM中的其他地方(它必须是,因为它比它更换的更大),所以它甚至不适合扩展和添加.
在我看来,这并不理想,这有两个原因.
首先,它很慢.我使用这些方法运行的测试用于求和单维或多维数组的内容,对于多维情况(a 和a 分别填充随机值,运行1000000次,温度)几乎是两倍长(714秒对371秒)高速缓存).int[1000000]int[100][100][100]int
public static long sumSingle(int[] arr) {
long total = 0;
for (int i=0; i<arr.length; i++)
total+=arr[i];
return total;
}
public static long sumMulti(int[][][] arr) {
long total = 0; …Run Code Online (Sandbox Code Playgroud) Java 9+ 中是否有更便宜的方法调用来保持其安全点?
JVM 在运行时移除安全点以提高效率,但这会使分析和监控代码变得更加困难。出于这个原因,我们特意在精心挑选的地方添加了琐碎的调用,以确保存在安全点。
public static void safepoint() {
if (IS_JAVA_9_PLUS)
Thread.holdsLock(""); // 100 ns on Java 11
else
Compiler.enable(); // 5 ns on Java 8
}
public static void optionalSafepoint() {
if (SAFEPOINT_ENABLED)
if (IS_JAVA_9_PLUS)
Thread.holdsLock("");
else
Compiler.enable();
}
Run Code Online (Sandbox Code Playgroud)
在 Java 8 上,这种开销很好,但Compiler.enable()在 Java 9+ 中被优化掉了,所以我们必须使用更昂贵的方法,或者不启用此功能。
编辑:除了分析器之外,我还使用了safepoint()从中获取更好的细节,Thread.getStackTrace()以便应用程序可以对其自身进行分析,例如,当执行操作需要很长时间时。
我们最近有一种情况,我们的一个生产JVM会随机冻结.Java进程正在烧毁CPU,但所有可见活动都将停止:没有日志输出,没有写入GC日志,没有响应任何网络请求等.进程将保持此状态直到重新启动.
原来,该org.mozilla.javascript.DToA类,某些输入调用时,会感到困惑,并呼吁BigInteger.pow着巨大的值(例如5 ^ 2147483647),这会触发JVM冻结.我的猜测是,一些大循环,可能是在java.math.BigInteger.multiplyToLen中,在循环中没有安全点检查的情况下进行了JIT.下次JVM需要暂停进行垃圾收集时,它会冻结,因为运行BigInteger代码的线程很长时间都不会达到安全点.
我的问题:将来,我如何诊断这样的安全点问题?杀-3没有产生任何输出; 我认为它依赖于安全点来生成准确的堆栈.是否有任何生产安全的工具可以从正在运行的JVM中提取堆栈而无需等待安全点?(在这种情况下,我真的很幸运,并设法抓住一组堆叠的痕迹BigInteger.pow被调用刚过,但在此之前它的工作它的方式到足够大的输入完全楔入JVM,没有运气中风,我我不确定我们怎么会诊断出这个问题.)
编辑:以下代码说明了问题.
// Spawn a background thread to compute an enormous number.
new Thread(){ @Override public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException ex) {
}
BigInteger.valueOf(5).pow(100000000);
}}.start();
// Loop, allocating memory and periodically logging progress, so illustrate GC pause times.
byte[] b;
for (int outer = 0; ; outer++) {
long startMs = System.currentTimeMillis();
for (int inner = 0; inner < 100000; inner++) {
b = new byte[1000];
}
System.out.println("Iteration " …Run Code Online (Sandbox Code Playgroud) double average = LongStream
.of(-4480186093928204294L, -1340542863544260591L, -6004296286240039273L)
.average()
.getAsDouble()
Run Code Online (Sandbox Code Playgroud)
这会返回,2.20723960999901594E18但我希望它会返回-3.9416750812375014E18.
另一方面,这会返回正确的结果(-1.4628605853333333E9):
double average = IntStream
.of(-1282256274, -1645263673, -1461061809)
.average()
.getAsDouble()
Run Code Online (Sandbox Code Playgroud)
为什么IntStream.average()总是返回正确的平均值,LongStream.average()有时却没有?
java ×4
jvm ×3
arrays ×1
benchmarking ×1
freeze ×1
java-11 ×1
java-8 ×1
java-stream ×1
jit ×1
jvm-hotspot ×1
performance ×1
stack-trace ×1