Array allocation and access on the Java Virtual Machine and memory contention

后端 未结 2 1829
刺人心
刺人心 2021-01-31 11:07

Observe the following definition of a thread subclass (the entire runnable Java source file is included at the end of the question for your convenience):

final c         


        
2条回答
  •  庸人自扰
    2021-01-31 11:40

    Solution

    Run the JVM with the -XX:+UseCondCardMark flag, available only in JDK7. This solves the problem.

    Explanation

    Essentially, most managed-heap environments use card tables to mark the areas of memory into which writes occurred. Such memory areas are marked as dirty in the card table once the write occurs. This information is needed for garbage collection - references of the non-dirty memory areas don't have to be scanned. A card is a contiguous block of memory, typically 512 bytes. A card table typically has 1 byte for each card - if this byte is set, the card is dirty. This means that a card table with 64 bytes covers 64 * 512 bytes of memory. And typically, the cache line size today is 64 bytes.

    So each time a write to an object field occurs, the byte of the corresponding card in the card table must be set as dirty. A useful optimization in single thread programs is to do this by simply marking the relevant byte - do a write each time. An alternative of first checking whether the byte is set and a conditional write requires an additional read and a conditional jump, which is slightly slower.

    However, this optimization can be catastrophic in the event that there are multiple processors writing to the memory, as neighbouring cards being written to require a write to neighbouring bytes in the card table. So the memory area being written to (the entry in the array above) is not in the same cache-line, which is the usual cause of memory contention. The real reason is that the dirty bytes which are written to are in the same cache line.

    What the above flag does is - it implements the card table dirty byte write by first checking if the byte is already set, and setting it only if it isn't. This way the memory contention happens only during the first write to that card - after that, only reads to that cache-line occur. Since the cache-line is only read, it can be replicated across multiple processors and they don't have to synchronize to read it.

    I've observed that this flag increases the running time some 15-20% in the 1-thread case.

    The -XX:+UseCondCardMark flag is explained in this blog post and this bug report.

    The relevant concurrency mailing list discussion: Array allocation and access on the JVM.

自定义标题
段落格式
字体
字号
代码语言
提交回复
热议问题