问题:我有一个方法可以编译超过8000字节的Java字节码.HotSpot有一个神奇的限制,使得JIT不会超过8000字节的方法.(是的,有一个庞大的方法是合理的.这是一个标记器循环.)该方法在库中,我不想要求库的用户必须配置HotSpot来停用魔术限制.
观察:反编译字节码表明Eclipse Java Compiler生成了许多无意义的getos.(javac甚至更糟.)也就是说,有些只能从跳跃中获得.显然,跳转到goto的跳转应该直接跳到goto跳转的地方,goto应该被消除.
问题:是否有针对Java 5类文件的字节码优化器,可以使无意义的跳转链变平,然后删除不必要的getos?
编辑:我的意思是:
8698: goto 8548
8701: goto 0
Run Code Online (Sandbox Code Playgroud)
显然,第二个goto只能通过跳转到8701到达,这可能也是直接跳转到0.
在第二次调查中,这种可疑模式更为常见:
4257: if_icmpne 4263
4260: goto 8704
4263: aload_0
Run Code Online (Sandbox Code Playgroud)
显然,人们希望编译器将"不等于"比较反转为"相等"比较,跳转到8704并消除goto.
注意:这是不是重复,请仔细阅读题目сarefully /sf/users/241389361/报价:
真正的问题是为什么代码有时会起作用.即使没有lambdas,问题也会重现.这让我觉得可能存在JVM错误.
在/sf/answers/3759645221/的评论中,我试图找出原因,为什么代码的行为从一个开始到另一个不同,并且该讨论的参与者给了我一些建议来创建一个单独的主题.
不要考虑以下源代码:
public class Test {
static {
System.out.println("static initializer: " + Thread.currentThread().getName());
final long SUM = IntStream.range(0, 5)
.parallel()
.mapToObj(i -> {
System.out.println("map: " + Thread.currentThread().getName() + " " + i);
return i;
})
.sum();
}
public static void main(String[] args) {
System.out.println("Finished");
}
}
Run Code Online (Sandbox Code Playgroud)
有时(几乎总是)它会导致死锁.
输出示例:
static initializer: main
map: main 2
map: ForkJoinPool.commonPool-worker-3 4
map: ForkJoinPool.commonPool-worker-3 3
map: ForkJoinPool.commonPool-worker-2 0
Run Code Online (Sandbox Code Playgroud)
但有时它会成功完成(非常罕见):
static initializer: main
map: main 2
map: main …
Run Code Online (Sandbox Code Playgroud) 我已将最大堆设置为8 GB.当我的程序开始使用大约6.4 GB(在VisualVM中报告)时,垃圾收集器开始占用大部分CPU,并且在进行~100 MB分配时程序与OutOfMemory崩溃.我在Windows上使用Oracle Java 1.7.0_21.
我的问题是,是否有GC选项可以帮助解决这个问题.除了-Xmx8g,我没有传递任何东西.
我的猜测是堆已经碎片化了,但GC应该不紧凑吗?
根据我可以收集的有关.NET和Java执行环境的信息,目前的情况如下:
现代Java VM能够执行连续重新编译,与分析相结合可以产生很大的性能改进.较旧的JVM使用JIT.本文中的更多信息:http: //www.ibm.com/developerworks/library/j-jtp12214/ ,尤其是:Java理论与实践:动态编译和性能测量
.NET使用JIT或NGEN生成本机代码,但是一旦生成本机代码,就不会执行进一步的(运行时)优化.
抛开基准并且无意升级圣战,这是否意味着Java Hotspot VM比.Net领先一代.这些在Java VM中使用的技术最终是否会进入.NET运行时?
我正在测试在32核心服务器上为Java和C#运行相同功能的许多线程的产生.我使用函数的1000次迭代运行应用程序,使用线程池对1,2,4,8,16或32个线程进行批处理.
在1,2,4,8和16个并发线程中Java至少是C#的两倍.但是,随着线程数量的增加,间隙关闭,32个线程C#的平均运行时间几乎相同,但Java偶尔需要2000ms(而两种语言通常运行时间约为400ms).在每个线程迭代所花费的时间内,Java开始变得更糟.
编辑这是Windows Server 2008
EDIT2我已经使用Executor Service线程池更改了下面的代码.我还安装了Java 7.
我在热点VM中设置了以下优化:
-XX:+ UseConcMarkSweepGC -Xmx 6000
但它仍然没有让事情变得更好.代码之间的唯一区别是我使用下面的线程池和我们使用的C#版本:
http://www.codeproject.com/Articles/7933/Smart-Thread-Pool
有没有办法让Java更加优化?Perhaos你可以解释为什么我看到这种性能大幅下降?
是否有更高效的Java线程池?
(请注意,我不是指改变测试功能)
import java.io.DataOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
public class PoolDemo {
static long FastestMemory = 2000000;
static long SlowestMemory = 0;
static long TotalTime;
static int[] FileArray;
static DataOutputStream outs;
static FileOutputStream fout;
static Byte myByte = 0;
public static void main(String[] args) throws InterruptedException, FileNotFoundException {
int Iterations = Integer.parseInt(args[0]);
int ThreadSize = Integer.parseInt(args[1]); …
Run Code Online (Sandbox Code Playgroud) 我有这个代码动态生成类并加载它
import javassist.CannotCompileException;
import javassist.ClassPool;
public class PermGenLeak {
private static final String PACKAGE_NAME = "com.jigarjoshi.permgenleak.";
public static void main(String[] args) throws CannotCompileException, InterruptedException {
for (int i = 0; i < Integer.MAX_VALUE; i++) {
ClassPool pool = ClassPool.getDefault();
pool.makeClass(PACKAGE_NAME + i).toClass();
Thread.sleep(3);
}
}
}
Run Code Online (Sandbox Code Playgroud)
我针对Java 7(jdk1.7.0_60)启动了这个类,正如预期的那样,它填满了PermGenSpace并且堆仍未使用
图像显示permgen使用超时,最后JVM终止
现在相同的代码针对Java 8(jdk1.8.0_40-ea)运行并且正如预期的那样它继续扩展本机内存(Metaspace)但令人惊讶的是,1g的Metaspace它在OldGen中消耗了3g的堆(随着时间的推移,几乎是Metaspace的3倍)
该图显示了Metaspace使用超时和系统内存使用示例
这封来自Jon Masamitsu的电子邮件和这张JEP门票说
interned
String
和Class stats以及一些misc数据已移至Heap
当它将更多类加载到Metaspace中时,究竟是什么导致了堆的增加?
我们知道,一些JIT允许重新排序对象初始化,例如,
someRef = new SomeObject();
Run Code Online (Sandbox Code Playgroud)
可以分解为以下步骤:
objRef = allocate space for SomeObject; //step1
call constructor of SomeObject; //step2
someRef = objRef; //step3
Run Code Online (Sandbox Code Playgroud)
JIT编译器可能会重新排序如下:
objRef = allocate space for SomeObject; //step1
someRef = objRef; //step3
call constructor of SomeObject; //step2
Run Code Online (Sandbox Code Playgroud)
即,步骤2和步骤3可以由JIT编译器重新排序.虽然这在理论上是有效的重新排序,但我无法使用x86平台下的Hotspot(jdk1.7)重现它.
那么,Hotspot JIT comipler是否可以重现任何指令重新排序?
更新:我使用以下命令在我的机器(Linux x86_64,JDK 1.8.0_40,i5-3210M)上进行了测试:
java -XX:-UseCompressedOops -XX:+UnlockDiagnosticVMOptions -XX:CompileCommand="print org.openjdk.jcstress.tests.unsafe.UnsafePublication::publish" -XX:CompileCommand="inline, org.openjdk.jcstress.tests.unsafe.UnsafePublication::publish" -XX:PrintAssemblyOptions=intel -jar tests-custom/target/jcstress.jar -f -1 -t .*UnsafePublication.* -v > log.txt
Run Code Online (Sandbox Code Playgroud)
我可以看到该工具报告的内容如下:
[1] 5可接受对象已发布,至少有1个字段可见.
这意味着观察者线程看到了一个未初始化的MyObject实例.
但是,我没有看到像@ Ivan那样生成的汇编代码:
0x00007f71d4a15e34: mov r11d,DWORD PTR …
Run Code Online (Sandbox Code Playgroud) 介绍:
在大学里,人们了解到Java(和类似语言)中的典型垃圾收集根是加载类的静态变量,当前运行线程的线程局部变量,"外部引用"(如JNI句柄)和GC特定(如old-to) -在世代垃圾收集器的Minor GCs期间的年轻指针.从理论上讲,这听起来并不难.
问题:
我读的热点的源代码和感兴趣的是如何在VM内检测到这些垃圾收集根,即,该方法是使用内部的JVM源代码访问所有的根源.
调查:
我发现psMarkSweep.cpp
属于各种GC实现的各种文件(例如)包含非常相似的结构.
以下是我认为涵盖强大根源的方法PSMarkSweep::mark_sweep_phase1
方法psMarkSweep.cpp
:
ParallelScavengeHeap::ParStrongRootsScope psrs;
Universe::oops_do(mark_and_push_closure());
JNIHandles::oops_do(mark_and_push_closure()); // Global (strong) JNI handles
CLDToOopClosure mark_and_push_from_cld(mark_and_push_closure());
MarkingCodeBlobClosure each_active_code_blob(mark_and_push_closure(), !CodeBlobToOopClosure::FixRelocations);
Threads::oops_do(mark_and_push_closure(), &mark_and_push_from_cld, &each_active_code_blob);
ObjectSynchronizer::oops_do(mark_and_push_closure());
FlatProfiler::oops_do(mark_and_push_closure());
Management::oops_do(mark_and_push_closure());
JvmtiExport::oops_do(mark_and_push_closure());
SystemDictionary::always_strong_oops_do(mark_and_push_closure());
ClassLoaderDataGraph::always_strong_cld_do(follow_cld_closure());
// Do not treat nmethods as strong roots for mark/sweep, since we can unload them.
//CodeCache::scavenge_root_nmethods_do(CodeBlobToOopClosure(mark_and_push_closure()));
Run Code Online (Sandbox Code Playgroud)
以下代码psScavenge.cpp
似乎为不同类型的GC根添加任务:
if (!old_gen->object_space()->is_empty()) {
// There are only old-to-young pointers …
Run Code Online (Sandbox Code Playgroud) 我从Java编译器收到一条我不明白的错误消息.我在OSX 10.6,10.9和Ubuntu 14.04上使用Java 6和7测试了我的代码.当我使用Eclipse调试器或解释器(使用-Xint选项)运行时,一切运行正常.否则,我收到以下消息:
Java 1.6:
Invalid memory access of location 0x8 rip=0x1024e9660
Run Code Online (Sandbox Code Playgroud)
Java 1.7:
#
# A fatal error has been detected by the Java Runtime Environment:
#
# SIGSEGV (0xb) at pc=0x000000010f7a8262, pid=20344, tid=18179
#
# JRE version: Java(TM) SE Runtime Environment (7.0_60-b19) (build 1.7.0_60-b19)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (24.60-b09 mixed mode bsd-amd64 compressed oops)
# Problematic frame:
# V [libjvm.dylib+0x3a8262] PhaseIdealLoop::idom_no_update(Node*) const+0x12
#
# Failed to write core dump. Core dumps have been …
Run Code Online (Sandbox Code Playgroud) (注意:正确答案必须超越复制).
在数百万次调用之后,quicksort1肯定比quicksort2更快,除了这个额外的arg之外,它们具有相同的代码.
代码在帖子的末尾.Spoiler:我还发现jit代码比224字节更胖,即使它实际上应该更简单(如字节代码大小告诉;请参阅下面的最后更新).
即使试图用一些微基准线束(JMH)来解决这种影响,性能差异仍然存在.
我在问:为什么生成的本机代码存在这样的差异,它在做什么?
通过向方法添加参数,它使它更快......!我知道gc/jit/warmup/etc效果.您可以按原样运行代码,也可以使用更大/更小的迭代计数.实际上,你甚至应该注释掉一个然后另一个性能测试并在不同的jvm实例中运行它们,只是为了证明它不是彼此之间的干扰.
字节码没有显示出太大的区别,除了明显的getstatic为sleft/sright,还有一个奇怪的'iload 4'而不是"iload_3"(和istore 4/istore_3)
到底他妈发生了什么?iload_3/istore_3真的比iload 4/istore 4慢吗?即使添加的getstatic调用仍然没有让它变慢,那要慢得多?我猜测静态字段是未使用的,因此jit可能只是跳过它.
无论如何,我的方面没有任何歧义,因为它总是可重复的,我正在寻找解释为什么javac/jit做了他们所做的,以及为什么性能受到如此大的影响.这些是相同的递归算法,具有相同的数据,相同的内存流失等等...如果我愿意,我无法进行更加孤立的更改,以显示可重复的运行时差异.
ENV:
java version "1.8.0_161"
Java(TM) SE Runtime Environment (build 1.8.0_161-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.161-b12, mixed mode)
(also tried and reproduced on java9)
on a 4 core i5 laptop 8GB ram.
windows 10 with the meltdown/specter patch.
Run Code Online (Sandbox Code Playgroud)
使用-verbose:gc -XX:+ PrintCompilation,没有gc和jit编译在C2(第4层)中已经稳定.
n = 20000时:
main]: qs1: 1561.3336199999999 ms (res=null)
main]: qs2: 1749.748416 ms (res=null)
main]: qs1: 1422.0767509999998 ms (res=null)
main]: qs2: 1700.4858689999999 ms (res=null)
main]: qs1: …
Run Code Online (Sandbox Code Playgroud) java ×10
jvm-hotspot ×10
jit ×3
jvm ×3
.net ×2
bytecode ×2
c# ×1
comparison ×1
concurrency ×1
deadlock ×1
eclipse ×1
goto ×1
heap-memory ×1
hotspot ×1
java-8 ×1
jvm-crash ×1
metaspace ×1
optimization ×1