From 351833d4031a3f49e9f2f0880481575f560e8aa6 Mon Sep 17 00:00:00 2001 From: DXwangg Date: Tue, 26 Sep 2023 16:04:24 +0800 Subject: [PATCH] Add 2309 feature to jdk8u382 on openEuler2309 --- ...t-in-XSLTC-for-specjvm-xml-transform.patch | 93 +- ...mp-should-show-more-clearly-when-a-t.patch | 392 +++ ...nostic-command-to-write-Linux-perf-m.patch | 277 ++ ...e-throws-NPE-parsing-CertificateRequ.patch | 25 + ...er-of-processes-to-errlog-file.patch.patch | 66 + ...destGCLog-option-is-added-to-control.patch | 89 + ...9-Tracking-malloc-pooled-memory-size.patch | 311 ++ ...lean-up-code-in-ptrQueue.-ch-pp-and-.patch | 645 ++++ ...ring-j.u.z.ZipFile-s-native-implemen.patch | 2911 +++++++++++++++++ ...j.u.z.ZipFile.getEntry-throws-AIOOBE.patch | 52 + ...fy-testcase-this-test-does-not-assum.patch | 79 + ...ile-implementation-no-longer-caches-.patch | 45 + ...st-DelegatingClassLoader-s-metadata-.patch | 100 + ...3-Improve-metaspace-chunk-allocation.patch | 2770 ++++++++++++++++ ...ile-reads-wrong-entry-size-from-ZIP6.patch | 274 ++ add-8226530-test-case-fixed.patch | 54 + ...time-memory-RunUnitTestsConcurrently.patch | 23 + ...d-reallocating-name-when-checking-fo.patch | 70 + ...g-IOException-of-Zip-to-ZipException.patch | 28 + ...t-collect_class-when-DynamicCDS-dump.patch | 26 + ...4-runtime-thread-signal-transfer-bug.patch | 153 + ...stance-klass-when-loading-from-jsa-f.patch | 60 + ..._fd-no-close-and-improve-KAEProvider.patch | 227 ++ ...ix-windows-build-Dynamic-CDS-failure.patch | 43 + ...e-Appcds-jsa-rw-region-deterministic.patch | 306 ++ openjdk-1.8.0.spec | 98 +- 26 files changed, 9118 insertions(+), 99 deletions(-) create mode 100644 8213397-Stack-dump-should-show-more-clearly-when-a-t.patch create mode 100644 8254723-Add-diagnostic-command-to-write-Linux-perf-m.patch create mode 100644 8295068-SSLEngine-throws-NPE-parsing-CertificateRequ.patch create mode 100644 Record-the-number-of-processes-to-errlog-file.patch.patch create mode 100644 The-OverWriteOldestGCLog-option-is-added-to-control.patch create mode 100644 add-0010-8301749-Tracking-malloc-pooled-memory-size.patch create mode 100644 add-6899049-G1-Clean-up-code-in-ptrQueue.-ch-pp-and-.patch create mode 100644 add-8142508-To-bring-j.u.z.ZipFile-s-native-implemen.patch create mode 100644 add-8146431-j.u.z.ZipFile.getEntry-throws-AIOOBE.patch create mode 100644 add-8147940-Modify-testcase-this-test-does-not-assum.patch create mode 100644 add-8170831-ZipFile-implementation-no-longer-caches-.patch create mode 100644 add-8191924-Adjust-DelegatingClassLoader-s-metadata-.patch create mode 100644 add-8198423-Improve-metaspace-chunk-allocation.patch create mode 100644 add-8226530-ZipFile-reads-wrong-entry-size-from-ZIP6.patch create mode 100644 add-8226530-test-case-fixed.patch create mode 100644 add-8227041-runtime-memory-RunUnitTestsConcurrently.patch create mode 100644 add-8242842-Avoid-reallocating-name-when-checking-fo.patch create mode 100644 add-Adapting-IOException-of-Zip-to-ZipException.patch create mode 100644 add-Do-not-collect_class-when-DynamicCDS-dump.patch create mode 100644 add-Fix-aarch64-runtime-thread-signal-transfer-bug.patch create mode 100644 add-add-Count-instance-klass-when-loading-from-jsa-f.patch create mode 100644 add-fix-lock_fd-no-close-and-improve-KAEProvider.patch create mode 100644 add-fix-windows-build-Dynamic-CDS-failure.patch create mode 100644 add-make-Appcds-jsa-rw-region-deterministic.patch diff --git a/0041-Reuse-translet-in-XSLTC-for-specjvm-xml-transform.patch b/0041-Reuse-translet-in-XSLTC-for-specjvm-xml-transform.patch index dcbde36..5b34c97 100644 --- a/0041-Reuse-translet-in-XSLTC-for-specjvm-xml-transform.patch +++ b/0041-Reuse-translet-in-XSLTC-for-specjvm-xml-transform.patch @@ -8,9 +8,7 @@ Subject: Reuse translet in XSLTC for XML hotspot/src/share/vm/runtime/arguments.cpp | 2 + hotspot/src/share/vm/runtime/arguments.hpp | 3 + .../src/share/vm/utilities/accessFlags.hpp | 1 + - .../transform/TestXmlTransletEnhance.java | 83 +++++++++++++++++++ - 8 files changed, 144 insertions(+), 3 deletions(-) - create mode 100644 jdk/test/javax/xml/jaxp/transform/TestXmlTransletEnhance.java + 7 files changed, 64 insertions(+), 3 deletions(-) diff --git a/hotspot/src/share/vm/classfile/classFileParser.cpp b/hotspot/src/share/vm/classfile/classFileParser.cpp index d8e99e622..b9fde38dc 100644 @@ -182,95 +180,6 @@ index bc56262d1..b20f0f740 100644 void set_has_finalizer() { atomic_set_bits(JVM_ACC_HAS_FINALIZER); } void set_has_final_method() { atomic_set_bits(JVM_ACC_HAS_FINAL_METHOD); } void set_is_cloneable() { atomic_set_bits(JVM_ACC_IS_CLONEABLE); } -diff --git a/jdk/test/javax/xml/jaxp/transform/TestXmlTransletEnhance.java b/jdk/test/javax/xml/jaxp/transform/TestXmlTransletEnhance.java -new file mode 100644 -index 000000000..73b848de8 ---- /dev/null -+++ b/jdk/test/javax/xml/jaxp/transform/TestXmlTransletEnhance.java -@@ -0,0 +1,83 @@ -+/* -+ * Copyright (c) Huawei Technologies Co., Ltd. 2012-2023. All rights reserved. -+ */ -+ -+ -+/* @test -+ * @summary a test for xml translet enhance -+ * @library /lib/testlibrary -+ * @run main TestXmlTransletEnhance -+ */ -+ -+import javax.xml.transform.TransformerFactory; -+import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; -+import java.lang.reflect.Field; -+import static jdk.testlibrary.Asserts.assertEquals; -+ -+public class TestXmlTransletEnhance { -+ static final boolean expectedResult = true; -+ -+ public static void main(String[] args) throws InterruptedException { -+ -+ Thread thread = new Mythread("BenchmarkThread xml "); -+ thread.start(); -+ thread.join(); -+ boolean ret = SharedData.getInstance().getResult(); -+ assertEquals(ret, expectedResult); -+ -+ } -+ -+ static class Mythread extends Thread { -+ Mythread(String name){ -+ super(name); -+ } -+ -+ @Override -+ public void run(){ -+ -+ try { -+ -+ TransformerFactory tf = TransformerFactory.newInstance(); -+ TransformerFactoryImpl transformer = new TransformerFactoryImpl(); -+ Class clazz = transformer.getClass(); -+ -+ Field generateTransletFiled = clazz.getDeclaredField("_generateTranslet"); -+ Field autoTransletFiled = clazz.getDeclaredField("_autoTranslet"); -+ -+ generateTransletFiled.setAccessible(true); -+ autoTransletFiled.setAccessible(true); -+ -+ boolean value1 = (boolean)generateTransletFiled.get(transformer); -+ boolean value2 = (boolean)autoTransletFiled.get(transformer); -+ -+ SharedData.getInstance().setResult(value1 && value2); -+ -+ } catch (NoSuchFieldException| IllegalAccessException | SecurityException | IllegalArgumentException e) { -+ e.printStackTrace(); -+ } -+ } -+ } -+ -+ static class SharedData { -+ private static SharedData instance; -+ private boolean result; -+ -+ private SharedData() { -+ } -+ -+ public static synchronized SharedData getInstance() { -+ if (instance == null) { -+ instance = new SharedData(); -+ } -+ return instance; -+ } -+ -+ public synchronized boolean getResult() { -+ return result; -+ } -+ -+ public synchronized void setResult(boolean result) { -+ this.result = result; -+ } -+ } -+} -- 2.22.0 diff --git a/8213397-Stack-dump-should-show-more-clearly-when-a-t.patch b/8213397-Stack-dump-should-show-more-clearly-when-a-t.patch new file mode 100644 index 0000000..b45886f --- /dev/null +++ b/8213397-Stack-dump-should-show-more-clearly-when-a-t.patch @@ -0,0 +1,392 @@ +From 3a774c78473c4fc3dcd1dc39f8c9daec4c5a6502 Mon Sep 17 00:00:00 2001 +Date: Thu, 21 Sep 2023 14:45:54 +0800 +Subject: 8213397-Stack-dump-should-show-more-clearly-when-a-t.patch + +--- + hotspot/src/share/vm/oops/instanceKlass.cpp | 23 +- + hotspot/src/share/vm/runtime/thread.cpp | 2 + + hotspot/src/share/vm/runtime/thread.hpp | 8 + + .../src/share/vm/runtime/thread.inline.hpp | 12 + + hotspot/src/share/vm/runtime/vframe.cpp | 8 + + .../TestThreadDumpClassInitMonitor.java | 217 ++++++++++++++++++ + 6 files changed, 259 insertions(+), 11 deletions(-) + create mode 100644 hotspot/test/runtime/Thread/TestThreadDumpClassInitMonitor.java + +diff --git a/hotspot/src/share/vm/oops/instanceKlass.cpp b/hotspot/src/share/vm/oops/instanceKlass.cpp +index 538645bbe..993778270 100644 +--- a/hotspot/src/share/vm/oops/instanceKlass.cpp ++++ b/hotspot/src/share/vm/oops/instanceKlass.cpp +@@ -920,25 +920,28 @@ void InstanceKlass::initialize_impl(instanceKlassHandle this_oop, TRAPS) { + + bool wait = false; + ++ assert(THREAD->is_Java_thread(), "non-JavaThread in initialize_impl"); ++ JavaThread* jt = (JavaThread*)THREAD; ++ + // refer to the JVM book page 47 for description of steps + // Step 1 + { + oop init_lock = this_oop->init_lock(); + ObjectLocker ol(init_lock, THREAD, init_lock != NULL); + +- Thread *self = THREAD; // it's passed the current thread +- + // Step 2 + // If we were to use wait() instead of waitInterruptibly() then + // we might end up throwing IE from link/symbol resolution sites + // that aren't expected to throw. This would wreak havoc. See 6320309. +- while(this_oop->is_being_initialized() && !this_oop->is_reentrant_initialization(self)) { +- wait = true; +- ol.waitUninterruptibly(CHECK); ++ while (this_oop->is_being_initialized() && !this_oop->is_reentrant_initialization(jt)) { ++ wait = true; ++ jt->set_class_to_be_initialized(this_oop()); ++ ol.waitUninterruptibly(jt); ++ jt->set_class_to_be_initialized(NULL); + } + + // Step 3 +- if (this_oop->is_being_initialized() && this_oop->is_reentrant_initialization(self)) { ++ if (this_oop->is_being_initialized() && this_oop->is_reentrant_initialization(jt)) { + DTRACE_CLASSINIT_PROBE_WAIT(recursive, InstanceKlass::cast(this_oop()), -1,wait); + return; + } +@@ -968,7 +971,7 @@ void InstanceKlass::initialize_impl(instanceKlassHandle this_oop, TRAPS) { + + // Step 6 + this_oop->set_init_state(being_initialized); +- this_oop->set_init_thread(self); ++ this_oop->set_init_thread(jt); + } + + // Step 7 +@@ -1004,8 +1007,6 @@ void InstanceKlass::initialize_impl(instanceKlassHandle this_oop, TRAPS) { + + // Step 8 + { +- assert(THREAD->is_Java_thread(), "non-JavaThread in initialize_impl"); +- JavaThread* jt = (JavaThread*)THREAD; + DTRACE_CLASSINIT_PROBE_WAIT(clinit, InstanceKlass::cast(this_oop()), -1,wait); + // Timer includes any side effects of class initialization (resolution, + // etc), but not recursive entry into call_class_initializer(). +@@ -1031,14 +1032,14 @@ void InstanceKlass::initialize_impl(instanceKlassHandle this_oop, TRAPS) { + CLEAR_PENDING_EXCEPTION; + // JVMTI has already reported the pending exception + // JVMTI internal flag reset is needed in order to report ExceptionInInitializerError +- JvmtiExport::clear_detected_exception((JavaThread*)THREAD); ++ JvmtiExport::clear_detected_exception(jt); + { + EXCEPTION_MARK; + this_oop->set_initialization_state_and_notify(initialization_error, THREAD); + CLEAR_PENDING_EXCEPTION; // ignore any exception thrown, class initialization error is thrown below + // JVMTI has already reported the pending exception + // JVMTI internal flag reset is needed in order to report ExceptionInInitializerError +- JvmtiExport::clear_detected_exception((JavaThread*)THREAD); ++ JvmtiExport::clear_detected_exception(jt); + } + DTRACE_CLASSINIT_PROBE_WAIT(error, InstanceKlass::cast(this_oop()), -1,wait); + if (e->is_a(SystemDictionary::Error_klass())) { +diff --git a/hotspot/src/share/vm/runtime/thread.cpp b/hotspot/src/share/vm/runtime/thread.cpp +index 2be226463..50543ac73 100644 +--- a/hotspot/src/share/vm/runtime/thread.cpp ++++ b/hotspot/src/share/vm/runtime/thread.cpp +@@ -1538,6 +1538,8 @@ void JavaThread::initialize() { + _popframe_preserved_args_size = 0; + _frames_to_pop_failed_realloc = 0; + ++_class_to_be_initialized = NULL; ++ + pd_initialize(); + } + +diff --git a/hotspot/src/share/vm/runtime/thread.hpp b/hotspot/src/share/vm/runtime/thread.hpp +index 220fe9316..1d3caf9aa 100644 +--- a/hotspot/src/share/vm/runtime/thread.hpp ++++ b/hotspot/src/share/vm/runtime/thread.hpp +@@ -1802,11 +1802,19 @@ public: + bool is_attaching_via_jni() const { return _jni_attach_state == _attaching_via_jni; } + bool has_attached_via_jni() const { return is_attaching_via_jni() || _jni_attach_state == _attached_via_jni; } + inline void set_done_attaching_via_jni(); ++ ++ // Stack dump assistance: Track the class we want to initialize but for which we have to wait ++ // on its init_lock() because it is already being initialized. ++ inline void set_class_to_be_initialized(InstanceKlass* k); ++ inline InstanceKlass* class_to_be_initialized() const; ++ + private: + // This field is used to determine if a thread has claimed + // a par_id: it is UINT_MAX if the thread has not claimed a par_id; + // otherwise its value is the par_id that has been claimed. + uint _claimed_par_id; ++ ++ InstanceKlass* _class_to_be_initialized; + public: + uint get_claimed_par_id() { return _claimed_par_id; } + void set_claimed_par_id(uint id) { _claimed_par_id = id;} +diff --git a/hotspot/src/share/vm/runtime/thread.inline.hpp b/hotspot/src/share/vm/runtime/thread.inline.hpp +index b05e0ec5c..d3ab3aba9 100644 +--- a/hotspot/src/share/vm/runtime/thread.inline.hpp ++++ b/hotspot/src/share/vm/runtime/thread.inline.hpp +@@ -74,4 +74,16 @@ inline void JavaThread::set_done_attaching_via_jni() { + OrderAccess::fence(); + } + ++// Allow tracking of class initialization monitor use ++inline void JavaThread::set_class_to_be_initialized(InstanceKlass* k) { ++ assert((k == NULL && _class_to_be_initialized != NULL) || ++ (k != NULL && _class_to_be_initialized == NULL), "incorrect usage"); ++ assert(this == Thread::current(), "Only the current thread can set this field"); ++ _class_to_be_initialized = k; ++} ++ ++inline InstanceKlass* JavaThread::class_to_be_initialized() const { ++ return _class_to_be_initialized; ++} ++ + #endif // SHARE_VM_RUNTIME_THREAD_INLINE_HPP +diff --git a/hotspot/src/share/vm/runtime/vframe.cpp b/hotspot/src/share/vm/runtime/vframe.cpp +index b3a6d0770..93d62ade7 100644 +--- a/hotspot/src/share/vm/runtime/vframe.cpp ++++ b/hotspot/src/share/vm/runtime/vframe.cpp +@@ -176,6 +176,14 @@ void javaVFrame::print_lock_info_on(outputStream* st, int frame_count) { + Klass* k = obj->klass(); + st->print_cr("\t- %s <" INTPTR_FORMAT "> (a %s)", "parking to wait for ", (address)obj, k->external_name()); + } ++ else if (thread()->osthread()->get_state() == OBJECT_WAIT) { ++ // We are waiting on an Object monitor but Object.wait() isn't the ++ // top-frame, so we should be waiting on a Class initialization monitor. ++ InstanceKlass* k = thread()->class_to_be_initialized(); ++ if (k != NULL) { ++ st->print_cr("\t- waiting on the Class initialization monitor for %s", k->external_name()); ++ } ++ } + } + + +diff --git a/hotspot/test/runtime/Thread/TestThreadDumpClassInitMonitor.java b/hotspot/test/runtime/Thread/TestThreadDumpClassInitMonitor.java +new file mode 100644 +index 000000000..8aa218efb +--- /dev/null ++++ b/hotspot/test/runtime/Thread/TestThreadDumpClassInitMonitor.java +@@ -0,0 +1,217 @@ ++/* ++ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++ ++/* ++ * @test ++ * @bug 8213397 8217337 ++ * @summary Check that the thread dump shows when a thread is blocked ++ * on a class initialization monitor ++ * ++ * @library /testlibrary ++ * @run main/othervm TestThreadDumpClassInitMonitor ++ */ ++ ++import com.oracle.java.testlibrary.*; ++ ++import java.io.IOException; ++import java.util.List; ++import java.lang.management.ManagementFactory; ++import java.lang.management.RuntimeMXBean; ++ ++public class TestThreadDumpClassInitMonitor { ++ // jstack tends to be closely bound to the VM that we are running ++ // so use getTestJDKTool() instead of getCompileJDKTool() or even ++ // getJDKTool() which can fall back to "compile.jdk". ++ final static String JSTACK = JDKToolFinder.getTestJDKTool("jstack"); ++ final static String PID = getPid(); ++ final static Thread current = Thread.currentThread(); ++ ++ /* ++ * This is the output we're looking for: ++ * ++ * "TestThread" #22 prio=5 os_prio=0 cpu=1.19ms elapsed=0.80s tid=0x00007f8f9405d800 nid=0x568b in Object.wait() [0x00007f8fd94d0000] ++ * java.lang.Thread.State: RUNNABLE ++ * Thread: 0x00007f8f9405d800 [0x568b] State: _at_safepoint _has_called_back 0 _at_poll_safepoint 0 // DEBUG ONLY ++ * JavaThread state: _thread_blocked // DEBUG ONLY ++ * at TestThreadDumpClassInitMonitor$Target$1.run(TestThreadDumpClassInitMonitor.java:69) ++ * - waiting on the Class initialization monitor for TestThreadDumpClassInitMonitor$Target ++ * ++ */ ++ final static String TEST_THREAD = "TestThread"; ++ final static String TEST_THREAD_ENTRY = "\"" + TEST_THREAD; ++ final static String IN_OBJECT_WAIT = "in Object.wait()"; ++ final static String THREAD_STATE = "java.lang.Thread.State: RUNNABLE"; ++ final static String THREAD_INFO = "Thread:"; // the details are not important ++ final static String JAVATHREAD_STATE = "JavaThread state: _thread_blocked"; ++ final static String CURRENT_METHOD = "at TestThreadDumpClassInitMonitor$Target$1.run"; ++ final static String WAIT_INFO = "- waiting on the Class initialization monitor for TestThreadDumpClassInitMonitor$Target"; ++ ++ volatile static boolean ready = false; ++ ++ static List stackDump; // jstack output as lines ++ ++ static class Target { ++ ++ static int field; ++ ++ // The main thread will initialize this class and so ++ // execute the actual test logic here. ++ static { ++ if (Thread.currentThread() != current) { ++ throw new Error("Initialization logic error"); ++ } ++ System.out.println("Initializing Target class in main thread"); ++ ++ Thread t = new Thread() { ++ public void run() { ++ System.out.println("Test thread about to access Target"); ++ ready = true; // tell main thread we're close ++ // This will block until the main thread completes ++ // static initialization of target ++ Target.field = 42; ++ System.out.println("Test thread done"); ++ } ++ }; ++ t.setName(TEST_THREAD); ++ t.start(); ++ ++ // We want to run jstack once the test thread is blocked but ++ // there's no programmatic way to detect that. So we check the flag ++ // that will be set just before it should block, then by the time ++ // we can exec jstack it should be ready. ++ try { ++ while (!ready) { ++ Thread.sleep(200); ++ } ++ } ++ catch (InterruptedException ie) { ++ throw new Error("Shouldn't happen"); ++ } ++ ++ // Now run jstack ++ try { ++ ProcessBuilder pb = new ProcessBuilder(JSTACK, PID); ++ OutputAnalyzer output = new OutputAnalyzer(pb.start()); ++ output.shouldHaveExitValue(0); ++ stackDump = output.asLines(); ++ } ++ catch (IOException ioe) { ++ throw new Error("Launching jstack failed", ioe); ++ } ++ } ++ } ++ ++ ++ public static void main(String[] args) throws Throwable { ++ // Implicitly run the main test logic ++ Target.field = 21; ++ ++ // Now check the output of jstack ++ try { ++ // product builds miss 2 lines of information in the stack ++ boolean isProduct = !Platform.isDebugBuild(); ++ int foundLines = 0; ++ parseStack: for (String line : stackDump) { ++ switch(foundLines) { ++ case 0: { ++ if (!line.startsWith(TEST_THREAD_ENTRY)) { ++ continue; ++ } ++ foundLines++; ++ if (!line.contains(IN_OBJECT_WAIT)) { ++ throw new Error("Unexpected initial stack line: " + line); ++ } ++ continue; ++ } ++ case 1: { ++ if (!line.trim().equals(THREAD_STATE)) { ++ throw new Error("Unexpected thread state line: " + line); ++ } ++ if (isProduct) { ++ foundLines += 3; ++ } else { ++ foundLines++; ++ } ++ continue; ++ } ++ case 2: { // Debug build ++ if (!line.startsWith(THREAD_INFO)) { ++ throw new Error("Unexpected thread info line: " + line); ++ } ++ foundLines++; ++ continue; ++ } ++ case 3: { // Debug build ++ if (!line.trim().equals(JAVATHREAD_STATE)) { ++ throw new Error("Unexpected JavaThread state line: " + line); ++ } ++ foundLines++; ++ continue; ++ } ++ case 4: { ++ if (!line.trim().startsWith(CURRENT_METHOD)) { ++ throw new Error("Unexpected current method line: " + line); ++ } ++ foundLines++; ++ continue; ++ } ++ case 5: { ++ if (!line.trim().equals(WAIT_INFO)) { ++ throw new Error("Unexpected monitor information line: " + line); ++ } ++ break parseStack; ++ } ++ default: throw new Error("Logic error in case statement"); ++ } ++ } ++ ++ if (foundLines == 0) { ++ throw new Error("Unexpected stack content - did not find line starting with " ++ + TEST_THREAD_ENTRY); ++ } ++ } ++ catch (Error e) { ++ // Dump the full stack trace on error so we can check the content ++ for (String line : stackDump) { ++ System.out.println(line); ++ } ++ throw e; ++ } ++ } ++ ++ // This helper relies on RuntimeMXBean.getName() returning a string ++ // that looks like this: 5436@mt-haku ++ // ++ // The testlibrary has tryFindJvmPid(), but that uses a separate ++ // process which is much more expensive for finding out your own PID. ++ // ++ static String getPid() { ++ RuntimeMXBean runtimebean = ManagementFactory.getRuntimeMXBean(); ++ String vmname = runtimebean.getName(); ++ int i = vmname.indexOf('@'); ++ if (i != -1) { ++ vmname = vmname.substring(0, i); ++ } ++ return vmname; ++ } ++} +\ No newline at end of file +-- +2.22.0 + diff --git a/8254723-Add-diagnostic-command-to-write-Linux-perf-m.patch b/8254723-Add-diagnostic-command-to-write-Linux-perf-m.patch new file mode 100644 index 0000000..c3e3ca9 --- /dev/null +++ b/8254723-Add-diagnostic-command-to-write-Linux-perf-m.patch @@ -0,0 +1,277 @@ +From 6a5759c82b869c4d931273609aa19eb1a84df8db Mon Sep 17 00:00:00 2001 +Date: Thu, 21 Sep 2023 15:12:51 +0800 +Subject: 8254723-Add-diagnostic-command-to-write-Linux-perf-m.patch + +--- + hotspot/src/os/linux/vm/globals_linux.hpp | 5 +- + hotspot/src/os/linux/vm/os_linux.cpp | 6 ++ + hotspot/src/share/vm/code/codeCache.cpp | 36 ++++++++ + hotspot/src/share/vm/code/codeCache.hpp | 1 + + hotspot/src/share/vm/runtime/java.cpp | 6 ++ + .../share/vm/services/diagnosticCommand.cpp | 7 ++ + .../share/vm/services/diagnosticCommand.hpp | 23 +++++ + .../test/serviceability/dcmd/PerfMapTest.java | 84 +++++++++++++++++++ + 8 files changed, 167 insertions(+), 1 deletion(-) + create mode 100644 hotspot/test/serviceability/dcmd/PerfMapTest.java + +diff --git a/hotspot/src/os/linux/vm/globals_linux.hpp b/hotspot/src/os/linux/vm/globals_linux.hpp +index f98bde41a..5cbe686d3 100644 +--- a/hotspot/src/os/linux/vm/globals_linux.hpp ++++ b/hotspot/src/os/linux/vm/globals_linux.hpp +@@ -55,7 +55,10 @@ + product(bool, PreferContainerQuotaForCPUCount, true, \ + "Calculate the container CPU availability based on the value" \ + " of quotas (if set), when true. Otherwise, use the CPU" \ +- " shares value, provided it is less than quota.") ++ " shares value, provided it is less than quota.") \ ++ \ ++ diagnostic(bool, DumpPerfMapAtExit, false, \ ++ "Write map file for Linux perf tool at exit") + + // + // Defines Linux-specific default values. The flags are available on all +diff --git a/hotspot/src/os/linux/vm/os_linux.cpp b/hotspot/src/os/linux/vm/os_linux.cpp +index a1cc85ca3..197b5c193 100644 +--- a/hotspot/src/os/linux/vm/os_linux.cpp ++++ b/hotspot/src/os/linux/vm/os_linux.cpp +@@ -5648,6 +5648,12 @@ jint os::init_2(void) + // initialize thread priority policy + prio_init(); + ++ if (DumpPerfMapAtExit && FLAG_IS_DEFAULT(UseCodeCacheFlushing)) { ++ // Disable code cache flushing to ensure the map file written at ++ // exit contains all nmethods generated during execution. ++ FLAG_SET_DEFAULT(UseCodeCacheFlushing, false); ++ } ++ + return JNI_OK; + } + +diff --git a/hotspot/src/share/vm/code/codeCache.cpp b/hotspot/src/share/vm/code/codeCache.cpp +index 37f24b5e9..97ad3ba79 100644 +--- a/hotspot/src/share/vm/code/codeCache.cpp ++++ b/hotspot/src/share/vm/code/codeCache.cpp +@@ -1033,3 +1033,39 @@ void CodeCache::log_state(outputStream* st) { + unallocated_capacity()); + } + ++#ifdef LINUX ++void CodeCache::write_perf_map() { ++ MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); ++ ++ // Perf expects to find the map file at /tmp/perf-.map. ++ char fname[32]; ++ jio_snprintf(fname, sizeof(fname), "/tmp/perf-%d.map", os::current_process_id()); ++ ++ fileStream fs(fname, "w"); ++ if (!fs.is_open()) { ++ DEBUG_ONLY(warning("[codecache] Failed to create %s for perf map", fname)); ++ return; ++ } ++ ++ FOR_ALL_ALIVE_BLOBS(cb) { ++ if (cb->is_nmethod()) { ++ nmethod *nm = (nmethod *) cb; ++ assert(!nm->is_unloaded(), "Tautology"); ++ ResourceMark rm; ++ const char* method_name = nm->method()->name_and_sig_as_C_string(); ++ fs.print_cr(INTPTR_FORMAT " " INTPTR_FORMAT " %s", ++ (intptr_t)cb->code_begin(), (intptr_t)cb->code_size(), ++ method_name); ++ } ++ if (cb->is_runtime_stub()) { ++ RuntimeStub *stub = (RuntimeStub *) cb; ++ ResourceMark rm; ++ const char* method_name = stub->name(); ++ fs.print_cr(INTPTR_FORMAT " " INTPTR_FORMAT " %s", ++ (intptr_t)cb->code_begin(), (intptr_t)cb->code_size(), ++ method_name); ++ } ++ } ++} ++ ++#endif // LINUX +diff --git a/hotspot/src/share/vm/code/codeCache.hpp b/hotspot/src/share/vm/code/codeCache.hpp +index ab1417b19..0aad2d648 100644 +--- a/hotspot/src/share/vm/code/codeCache.hpp ++++ b/hotspot/src/share/vm/code/codeCache.hpp +@@ -158,6 +158,7 @@ class CodeCache : AllStatic { + static void print_trace(const char* event, CodeBlob* cb, int size = 0) PRODUCT_RETURN; + static void print_summary(outputStream* st, bool detailed = true); // Prints a summary of the code cache usage + static void log_state(outputStream* st); ++ LINUX_ONLY(static void write_perf_map();) + + // Dcmd (Diagnostic commands) + static void print_codelist(outputStream* st); +diff --git a/hotspot/src/share/vm/runtime/java.cpp b/hotspot/src/share/vm/runtime/java.cpp +index 5a628b73e..fec8fb94d 100644 +--- a/hotspot/src/share/vm/runtime/java.cpp ++++ b/hotspot/src/share/vm/runtime/java.cpp +@@ -537,6 +537,12 @@ void before_exit(JavaThread * thread) { + BytecodeHistogram::print(); + } + ++#ifdef LINUX ++ if (DumpPerfMapAtExit) { ++ CodeCache::write_perf_map(); ++ } ++#endif ++ + if (JvmtiExport::should_post_thread_life()) { + JvmtiExport::post_thread_end(thread); + } +diff --git a/hotspot/src/share/vm/services/diagnosticCommand.cpp b/hotspot/src/share/vm/services/diagnosticCommand.cpp +index 416dc77ce..f8f6ad546 100644 +--- a/hotspot/src/share/vm/services/diagnosticCommand.cpp ++++ b/hotspot/src/share/vm/services/diagnosticCommand.cpp +@@ -36,6 +36,7 @@ + #include "services/management.hpp" + #include "utilities/macros.hpp" + #include "oops/objArrayOop.hpp" ++#include "code/codeCache.hpp" + + #ifdef LINUX + #include "trimCHeapDCmd.hpp" +@@ -81,6 +82,7 @@ void DCmdRegistrant::register_dcmds(){ + #ifdef LINUX + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); ++ DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); + #endif // LINUX + + // Enhanced JMX Agent Support +@@ -868,3 +870,8 @@ void CodeCacheDCmd::execute(DCmdSource source, TRAPS) { + VMThread::execute(&printCodeCacheOp); + } + ++#ifdef LINUX ++void PerfMapDCmd::execute(DCmdSource source, TRAPS) { ++ CodeCache::write_perf_map(); ++} ++#endif // LINUX +\ No newline at end of file +diff --git a/hotspot/src/share/vm/services/diagnosticCommand.hpp b/hotspot/src/share/vm/services/diagnosticCommand.hpp +index 3733fa7f7..d446aab4e 100644 +--- a/hotspot/src/share/vm/services/diagnosticCommand.hpp ++++ b/hotspot/src/share/vm/services/diagnosticCommand.hpp +@@ -590,4 +590,27 @@ public: + virtual void execute(DCmdSource source, TRAPS); + }; + ++#ifdef LINUX ++class PerfMapDCmd : public DCmd { ++public: ++ PerfMapDCmd(outputStream* output, bool heap) : DCmd(output, heap) {} ++ static const char* name() { ++ return "Compiler.perfmap"; ++ } ++ static const char* description() { ++ return "Write map file for Linux perf tool."; ++ } ++ static const char* impact() { ++ return "Low"; ++ } ++ static const JavaPermission permission() { ++ JavaPermission p = {"java.lang.management.ManagementPermission", ++ "monitor", NULL}; ++ return p; ++ } ++ static int num_arguments() { return 0; } ++ virtual void execute(DCmdSource source, TRAPS); ++}; ++#endif // LINUX ++ + #endif // SHARE_VM_SERVICES_DIAGNOSTICCOMMAND_HPP +diff --git a/hotspot/test/serviceability/dcmd/PerfMapTest.java b/hotspot/test/serviceability/dcmd/PerfMapTest.java +new file mode 100644 +index 000000000..1807b2a7f +--- /dev/null ++++ b/hotspot/test/serviceability/dcmd/PerfMapTest.java +@@ -0,0 +1,84 @@ ++/* ++ * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2020, Arm Limited. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++ ++/* ++ * @test PerfMapTest ++ * @bug 8254723 ++ * @requires os.family == "linux" ++ * @library /testlibrary ++ * @run testng/othervm PerfMapTest ++ * @summary Test of diagnostic command Compiler.perfmap ++ */ ++ ++import org.testng.annotations.Test; ++import org.testng.Assert; ++ ++import com.oracle.java.testlibrary.OutputAnalyzer; ++import com.oracle.java.testlibrary.CommandExecutor; ++import com.oracle.java.testlibrary.JMXExecutor; ++import com.oracle.java.testlibrary.ProcessTools; ++ ++import java.io.IOException; ++import java.nio.file.Files; ++import java.nio.file.Path; ++import java.nio.file.Paths; ++import java.util.regex.Matcher; ++import java.util.regex.Pattern; ++ ++/** ++ * Call jcmd Compiler.perfmap and check the output file has the expected ++ * format. ++ */ ++public class PerfMapTest { ++ ++ static final Pattern LINE_PATTERN = ++ Pattern.compile("^((?:0x)?\\p{XDigit}+)\\s+((?:0x)?\\p{XDigit}+)\\s+(.*)$"); ++ ++ public void run(CommandExecutor executor) throws Exception { ++ OutputAnalyzer output = executor.execute("Compiler.perfmap"); ++ ++ output.stderrShouldBeEmpty(); ++ output.stdoutShouldBeEmpty(); ++ ++ final long pid = ProcessTools.getProcessId(); ++ final Path path = Paths.get(String.format("/tmp/perf-%d.map", pid)); ++ ++ Assert.assertTrue(Files.exists(path)); ++ ++ // Sanity check the file contents ++ try { ++ for (String entry : Files.readAllLines(path)) { ++ Matcher m = LINE_PATTERN.matcher(entry); ++ Assert.assertTrue(m.matches(), "Invalid file format: " + entry); ++ } ++ } catch (IOException e) { ++ Assert.fail(e.toString()); ++ } ++ } ++ ++ @Test ++ public void jmx() throws Exception { ++ run(new JMXExecutor()); ++ } ++} +-- +2.22.0 + diff --git a/8295068-SSLEngine-throws-NPE-parsing-CertificateRequ.patch b/8295068-SSLEngine-throws-NPE-parsing-CertificateRequ.patch new file mode 100644 index 0000000..ff03757 --- /dev/null +++ b/8295068-SSLEngine-throws-NPE-parsing-CertificateRequ.patch @@ -0,0 +1,25 @@ +From 559bf67072bcf7fe854d03aebdfb49e02bd75899 Mon Sep 17 00:00:00 2001 +From: DXwangg +Date: Mon, 25 Sep 2023 09:50:38 +0800 +Subject: [PATCH] 8295068: SSLEngine throws NPE parsing CertificateRequests + +--- + jdk/src/share/classes/sun/security/ssl/CertificateRequest.java | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/jdk/src/share/classes/sun/security/ssl/CertificateRequest.java b/jdk/src/share/classes/sun/security/ssl/CertificateRequest.java +index 475ec42c..ad0325e4 100644 +--- a/jdk/src/share/classes/sun/security/ssl/CertificateRequest.java ++++ b/jdk/src/share/classes/sun/security/ssl/CertificateRequest.java +@@ -135,7 +135,7 @@ final class CertificateRequest { + ArrayList keyTypes = new ArrayList<>(3); + for (byte id : ids) { + ClientCertificateType cct = ClientCertificateType.valueOf(id); +- if (cct.isAvailable) { ++ if (cct != null && cct.isAvailable) { + keyTypes.add(cct.keyAlgorithm); + } + } +-- +2.39.0 + diff --git a/Record-the-number-of-processes-to-errlog-file.patch.patch b/Record-the-number-of-processes-to-errlog-file.patch.patch new file mode 100644 index 0000000..5e6e66b --- /dev/null +++ b/Record-the-number-of-processes-to-errlog-file.patch.patch @@ -0,0 +1,66 @@ +From 752494be2626cb819c92269e26f53833b2538160 Mon Sep 17 00:00:00 2001 +Date: Thu, 21 Sep 2023 14:46:56 +0800 +Subject: Record-the-number-of-processes-to-errlog-file.patch + +--- + hotspot/src/os/linux/vm/os_linux.cpp | 24 ++++++++++++++++++++++++ + hotspot/src/os/linux/vm/os_linux.hpp | 1 + + 2 files changed, 25 insertions(+) + +diff --git a/hotspot/src/os/linux/vm/os_linux.cpp b/hotspot/src/os/linux/vm/os_linux.cpp +index 72839eb5a..a1cc85ca3 100644 +--- a/hotspot/src/os/linux/vm/os_linux.cpp ++++ b/hotspot/src/os/linux/vm/os_linux.cpp +@@ -2256,6 +2256,10 @@ void os::print_os_info(outputStream* st) { + + os::Posix::print_load_average(st); + ++ if (ExtensiveErrorReports) { ++ os::Linux::print_system_process_count(st); ++ } ++ + os::Linux::print_system_memory_info(st); + st->cr(); + +@@ -2323,6 +2327,26 @@ void os::Linux::print_libversion_info(outputStream* st) { + st->cr(); + } + ++void os::Linux::print_system_process_count(outputStream* st) { ++ // system process count ++ DIR *dir = opendir("/proc"); ++ if (dir == NULL) { ++ return; ++ } ++ ++ st->print("system process count:"); ++ uint count = 0; ++ struct dirent *ptr; ++ while ((ptr = readdir(dir)) != NULL) { ++ if(ptr->d_type == DT_DIR && isdigit((ptr->d_name)[0])) { ++ count++; ++ } ++ } ++ (void) closedir(dir); ++ st->print("%u", count); ++ st->cr(); ++} ++ + void os::Linux::print_system_memory_info(outputStream* st) { + st->print("\n/proc/meminfo:\n"); + _print_ascii_file("/proc/meminfo", st); +diff --git a/hotspot/src/os/linux/vm/os_linux.hpp b/hotspot/src/os/linux/vm/os_linux.hpp +index a516335d2..19dde2e58 100644 +--- a/hotspot/src/os/linux/vm/os_linux.hpp ++++ b/hotspot/src/os/linux/vm/os_linux.hpp +@@ -124,6 +124,7 @@ class Linux { + static void print_container_info(outputStream* st); + static void print_distro_info(outputStream* st); + static void print_libversion_info(outputStream* st); ++ static void print_system_process_count(outputStream* st); + static void print_proc_sys_info(outputStream* st); + + public: +-- +2.22.0 + diff --git a/The-OverWriteOldestGCLog-option-is-added-to-control.patch b/The-OverWriteOldestGCLog-option-is-added-to-control.patch new file mode 100644 index 0000000..5130e9e --- /dev/null +++ b/The-OverWriteOldestGCLog-option-is-added-to-control.patch @@ -0,0 +1,89 @@ +From 2fd15967a7974d1d61cc7aa706c82733c572964d Mon Sep 17 00:00:00 2001 +Date: Thu, 21 Sep 2023 15:14:11 +0800 +Subject: The-OverWriteOldestGCLog-option-is-added-to-control + +--- + hotspot/src/share/vm/runtime/globals.hpp | 3 ++ + hotspot/src/share/vm/utilities/ostream.cpp | 45 ++++++++++++++++++++++ + 2 files changed, 48 insertions(+) + +diff --git a/hotspot/src/share/vm/runtime/globals.hpp b/hotspot/src/share/vm/runtime/globals.hpp +index 4f649bd45..fdd9db149 100644 +--- a/hotspot/src/share/vm/runtime/globals.hpp ++++ b/hotspot/src/share/vm/runtime/globals.hpp +@@ -2571,6 +2571,9 @@ class CommandLineFlags { + "GC log file size, requires UseGCLogFileRotation. " \ + "Set to 0 to only trigger rotation via jcmd") \ + \ ++ product(bool, OverWriteOldestGCLog, false, \ ++ "Over write the oldest gclog") \ ++ \ + /* JVMTI heap profiling */ \ + \ + diagnostic(bool, TraceJVMTIObjectTagging, false, \ +diff --git a/hotspot/src/share/vm/utilities/ostream.cpp b/hotspot/src/share/vm/utilities/ostream.cpp +index f4e127145..133b5a7c0 100644 +--- a/hotspot/src/share/vm/utilities/ostream.cpp ++++ b/hotspot/src/share/vm/utilities/ostream.cpp +@@ -843,6 +843,48 @@ gcLogFileStream::~gcLogFileStream() { + delete _file_lock; + } + ++static uintx next_file_number(const char* filename) { ++ uintx next_num; ++ uintx index; ++ char gclog[JVM_MAXPATHLEN]; ++ struct stat st; ++ long oldestTime = LONG_MAX; ++ bool normal_file_exist; ++ bool current_file_exist; ++ for (index = 0; index < NumberOfGCLogFiles; ++index) { ++ // normal gc log file ++ jio_snprintf(gclog, JVM_MAXPATHLEN, "%s.%d", filename, index); ++ normal_file_exist = (os::stat(gclog, &st) == 0); ++ if (normal_file_exist && oldestTime > st.st_mtime) { ++ oldestTime = st.st_mtime; ++ next_num = index; ++ } ++ ++ // current gc log file ++ jio_snprintf(gclog, JVM_MAXPATHLEN, "%s.%d" CURRENTAPPX, filename, index); ++ current_file_exist = (os::stat(gclog, &st) == 0); ++ if (current_file_exist && oldestTime > st.st_mtime) { ++ oldestTime = st.st_mtime; ++ next_num = index; ++ } ++ ++ // Stop looking if we find an unused file name ++ if (!normal_file_exist && !current_file_exist) { ++ next_num = index; ++ break; ++ } ++ } ++ // remove the existing normal file ++ char exist_file_name[JVM_MAXPATHLEN]; ++ jio_snprintf(exist_file_name, JVM_MAXPATHLEN, "%s.%d", filename, next_num); ++ if (access(exist_file_name, F_OK) == 0) { ++ if (remove(exist_file_name) != 0) { ++ warning("Could not delete existing normal file %s\n", exist_file_name); ++ } ++ } ++ return next_num; ++} ++ + gcLogFileStream::gcLogFileStream(const char* file_name) : _file_lock(NULL) { + _cur_file_num = 0; + _bytes_written = 0L; +@@ -857,6 +899,9 @@ gcLogFileStream::gcLogFileStream(const char* file_name) : _file_lock(NULL) { + + // gc log file rotation + if (UseGCLogFileRotation && NumberOfGCLogFiles > 1) { ++ if (OverWriteOldestGCLog) { ++ _cur_file_num = next_file_number(_file_name); ++ } + char tempbuf[JVM_MAXPATHLEN]; + jio_snprintf(tempbuf, sizeof(tempbuf), "%s.%d" CURRENTAPPX, _file_name, _cur_file_num); + _file = fopen(tempbuf, "w"); +-- +2.22.0 + diff --git a/add-0010-8301749-Tracking-malloc-pooled-memory-size.patch b/add-0010-8301749-Tracking-malloc-pooled-memory-size.patch new file mode 100644 index 0000000..5e3056b --- /dev/null +++ b/add-0010-8301749-Tracking-malloc-pooled-memory-size.patch @@ -0,0 +1,311 @@ +From fe0409dd4a7b26a623d2e0ad77be635b20f11d29 Mon Sep 17 00:00:00 2001 +Date: Thu, 21 Sep 2023 14:44:51 +0800 +Subject: add 0010-8301749-Tracking-malloc-pooled-memory-size + +--- + hotspot/src/os/linux/vm/mallocInfoDcmd.cpp | 62 +++++++++++++++++++ + hotspot/src/os/linux/vm/mallocInfoDcmd.hpp | 51 +++++++++++++++ + hotspot/src/os/linux/vm/os_linux.cpp | 10 +++ + hotspot/src/os/linux/vm/os_linux.hpp | 4 ++ + .../share/vm/services/diagnosticCommand.cpp | 2 + + .../serviceability/dcmd/MallocInfoTest.java | 54 ++++++++++++++++ + .../com/oracle/java/testlibrary/Platform.java | 18 +++++- + 7 files changed, 199 insertions(+), 2 deletions(-) + create mode 100644 hotspot/src/os/linux/vm/mallocInfoDcmd.cpp + create mode 100644 hotspot/src/os/linux/vm/mallocInfoDcmd.hpp + create mode 100644 hotspot/test/serviceability/dcmd/MallocInfoTest.java + +diff --git a/hotspot/src/os/linux/vm/mallocInfoDcmd.cpp b/hotspot/src/os/linux/vm/mallocInfoDcmd.cpp +new file mode 100644 +index 000000000..4e50a8e13 +--- /dev/null ++++ b/hotspot/src/os/linux/vm/mallocInfoDcmd.cpp +@@ -0,0 +1,62 @@ ++/* ++ * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#include "precompiled.hpp" ++#include "mallocInfoDcmd.hpp" ++#include "os_linux.hpp" ++#include "utilities/globalDefinitions.hpp" ++#include "utilities/ostream.hpp" ++ ++#include ++ ++const char* malloc_info_unavailable = "Error: malloc_info(3) not available."; ++ ++void MallocInfoDcmd::execute(DCmdSource source, TRAPS) { ++#ifdef __GLIBC__ ++ char* buf; ++ size_t size; ++ FILE* stream = ::open_memstream(&buf, &size); ++ if (stream == NULL) { ++ _output->print_cr("Error: Could not call malloc_info(3)"); ++ return; ++ } ++ ++ int err = os::Linux::malloc_info(stream); ++ if (err == 0) { ++ fflush(stream); ++ _output->print_raw(buf); ++ _output->cr(); ++ } else if (err == -1) { ++ _output->print_cr("Error: %s", strerror(errno)); ++ } else if (err == -2) { ++ _output->print_cr("%s", malloc_info_unavailable); ++ } else { ++ ShouldNotReachHere(); ++ } ++ ::fclose(stream); ++ ::free(buf); ++#else ++ _output->print_cr(malloc_info_unavailable); ++#endif // __GLIBC__ ++} +diff --git a/hotspot/src/os/linux/vm/mallocInfoDcmd.hpp b/hotspot/src/os/linux/vm/mallocInfoDcmd.hpp +new file mode 100644 +index 000000000..deb154415 +--- /dev/null ++++ b/hotspot/src/os/linux/vm/mallocInfoDcmd.hpp +@@ -0,0 +1,51 @@ ++/* ++ * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ * ++ */ ++ ++#ifndef OS_LINUX_MALLOCINFODCMD_HPP ++#define OS_LINUX_MALLOCINFODCMD_HPP ++ ++#include "services/diagnosticCommand.hpp" ++ ++class outputStream; ++ ++class MallocInfoDcmd : public DCmd { ++public: ++ MallocInfoDcmd(outputStream* output, bool heap) : DCmd(output, heap) {} ++ static const char* name() { ++ return "System.native_heap_info"; ++ } ++ static const char* description() { ++ return "Attempts to output information regarding native heap usage through malloc_info(3). If unsuccessful outputs \"Error: \" and a reason."; ++ } ++ static const char* impact() { ++ return "Low"; ++ } ++ static const JavaPermission permission() { ++ JavaPermission p = { "java.lang.management.ManagementPermission", "monitor", NULL }; ++ return p; ++ } ++ void execute(DCmdSource source, TRAPS); ++}; ++ ++#endif // OS_LINUX_MALLOCINFODCMD_HPP +diff --git a/hotspot/src/os/linux/vm/os_linux.cpp b/hotspot/src/os/linux/vm/os_linux.cpp +index cf3a166aa..72839eb5a 100644 +--- a/hotspot/src/os/linux/vm/os_linux.cpp ++++ b/hotspot/src/os/linux/vm/os_linux.cpp +@@ -177,6 +177,8 @@ typedef struct glibc_mallinfo (*mallinfo_func_t)(void); + typedef struct os::Linux::glibc_mallinfo2 (*mallinfo2_func_t)(void); + static mallinfo_func_t g_mallinfo = NULL; + static mallinfo2_func_t g_mallinfo2 = NULL; ++typedef int (*malloc_info_func_t)(int options, FILE *stream); ++static malloc_info_func_t g_malloc_info = NULL; + #endif // __GLIBC__ + + static jlong initial_time_count=0; +@@ -5416,6 +5418,7 @@ void os::init(void) { + #ifdef __GLIBC__ + g_mallinfo = CAST_TO_FN_PTR(mallinfo_func_t, dlsym(RTLD_DEFAULT, "mallinfo")); + g_mallinfo2 = CAST_TO_FN_PTR(mallinfo2_func_t, dlsym(RTLD_DEFAULT, "mallinfo2")); ++ g_malloc_info = CAST_TO_FN_PTR(malloc_info_func_t, dlsym(RTLD_DEFAULT, "malloc_info")); + #endif // __GLIBC__ + + // _main_thread points to the thread that created/loaded the JVM. +@@ -7072,6 +7075,13 @@ os::Linux::mallinfo_retval_t os::Linux::get_mallinfo(glibc_mallinfo2* out) { + } + return os::Linux::ok; + } ++ ++int os::Linux::malloc_info(FILE* stream) { ++ if (g_malloc_info == NULL) { ++ return -2; ++ } ++ return g_malloc_info(0, stream); ++} + #endif // __GLIBC__ + + // Trim-native support +diff --git a/hotspot/src/os/linux/vm/os_linux.hpp b/hotspot/src/os/linux/vm/os_linux.hpp +index 39a2b4989..a516335d2 100644 +--- a/hotspot/src/os/linux/vm/os_linux.hpp ++++ b/hotspot/src/os/linux/vm/os_linux.hpp +@@ -488,6 +488,10 @@ public: + // If we only have mallinfo(), values may be 32-bit truncated, which is signaled via + // "ok_but_possibly_wrapped". + static mallinfo_retval_t get_mallinfo(glibc_mallinfo2* out); ++ ++ // Calls out to GNU extension malloc_info if available ++ // otherwise does nothing and returns -2. ++ static int malloc_info(FILE* stream); + #endif + + static bool isbound_to_all_node() { +diff --git a/hotspot/src/share/vm/services/diagnosticCommand.cpp b/hotspot/src/share/vm/services/diagnosticCommand.cpp +index 50050a169..416dc77ce 100644 +--- a/hotspot/src/share/vm/services/diagnosticCommand.cpp ++++ b/hotspot/src/share/vm/services/diagnosticCommand.cpp +@@ -39,6 +39,7 @@ + + #ifdef LINUX + #include "trimCHeapDCmd.hpp" ++#include "mallocInfoDcmd.hpp" + #endif + + PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC +@@ -79,6 +80,7 @@ void DCmdRegistrant::register_dcmds(){ + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); + #ifdef LINUX + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); ++ DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); + #endif // LINUX + + // Enhanced JMX Agent Support +diff --git a/hotspot/test/serviceability/dcmd/MallocInfoTest.java b/hotspot/test/serviceability/dcmd/MallocInfoTest.java +new file mode 100644 +index 000000000..bc8ab3ef4 +--- /dev/null ++++ b/hotspot/test/serviceability/dcmd/MallocInfoTest.java +@@ -0,0 +1,54 @@ ++/* ++ * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++ ++import org.testng.annotations.Test; ++import com.oracle.java.testlibrary.*; ++ ++/* ++ * @test ++ * @summary Test of diagnostic command System.native_heap_info ++ * @library /testlibrary ++ * @requires (os.family=="linux") ++ * @modules java.base/jdk.internal.misc ++ * java.compiler ++ * java.management ++ * jdk.internal.jvmstat/sun.jvmstat.monitor ++ * @run testng MallocInfoTest ++ */ ++public class MallocInfoTest { ++ public void run(CommandExecutor executor) { ++ OutputAnalyzer output = executor.execute("System.native_heap_info"); ++ if (!Platform.isMusl()) { ++ output.shouldNotContain("Error: "); ++ output.shouldContain("(buf[i]); + if (card_ptr != NULL) { + // Set the entry to null, so we don't do it again (via the test + // above) if we reconsider this buffer. +- if (consume) buf[ind] = NULL; +- if (!cl->do_card_ptr(card_ptr, worker_i)) return false; ++ if (consume) { ++ buf[i] = NULL; ++ } ++ if (!cl->do_card_ptr(card_ptr, worker_i)) { ++ return false; ++ } + } + } + return true; +@@ -71,7 +89,7 @@ bool DirtyCardQueue::apply_closure_to_buffer(CardTableEntryClosure* cl, + DirtyCardQueueSet::DirtyCardQueueSet(bool notify_when_complete) : + PtrQueueSet(notify_when_complete), + _mut_process_closure(NULL), +- _shared_dirty_card_queue(this, true /*perm*/), ++ _shared_dirty_card_queue(this, true /* permanent */), + _free_ids(NULL), + _processed_buffers_mut(0), _processed_buffers_rs_thread(0) + { +@@ -83,13 +101,19 @@ uint DirtyCardQueueSet::num_par_ids() { + return (uint)os::initial_active_processor_count(); + } + +-void DirtyCardQueueSet::initialize(CardTableEntryClosure* cl, Monitor* cbl_mon, Mutex* fl_lock, ++void DirtyCardQueueSet::initialize(CardTableEntryClosure* cl, ++ Monitor* cbl_mon, ++ Mutex* fl_lock, + int process_completed_threshold, + int max_completed_queue, +- Mutex* lock, PtrQueueSet* fl_owner) { ++ Mutex* lock, ++ DirtyCardQueueSet* fl_owner) { + _mut_process_closure = cl; +- PtrQueueSet::initialize(cbl_mon, fl_lock, process_completed_threshold, +- max_completed_queue, fl_owner); ++ PtrQueueSet::initialize(cbl_mon, ++ fl_lock, ++ process_completed_threshold, ++ max_completed_queue, ++ fl_owner); + set_buffer_size(G1UpdateBufferSize); + _shared_dirty_card_queue.set_lock(lock); + _free_ids = new FreeIdSet((int) num_par_ids(), _cbl_mon); +@@ -103,7 +127,7 @@ void DirtyCardQueueSet::iterate_closure_all_threads(CardTableEntryClosure* cl, + bool consume, + uint worker_i) { + assert(SafepointSynchronize::is_at_safepoint(), "Must be at safepoint."); +- for(JavaThread* t = Threads::first(); t; t = t->next()) { ++ for (JavaThread* t = Threads::first(); t; t = t->next()) { + bool b = t->dirty_card_queue().apply_closure(cl, consume); + guarantee(b, "Should not be interrupted."); + } +@@ -160,8 +184,7 @@ bool DirtyCardQueueSet::mut_process_buffer(void** buf) { + } + + +-BufferNode* +-DirtyCardQueueSet::get_completed_buffer(int stop_at) { ++BufferNode* DirtyCardQueueSet::get_completed_buffer(int stop_at) { + BufferNode* nd = NULL; + MutexLockerEx x(_cbl_mon, Mutex::_no_safepoint_check_flag); + +@@ -178,12 +201,11 @@ DirtyCardQueueSet::get_completed_buffer(int stop_at) { + _n_completed_buffers--; + assert(_n_completed_buffers >= 0, "Invariant"); + } +- debug_only(assert_completed_buffer_list_len_correct_locked()); ++ DEBUG_ONLY(assert_completed_buffer_list_len_correct_locked()); + return nd; + } + +-bool DirtyCardQueueSet:: +-apply_closure_to_completed_buffer_helper(CardTableEntryClosure* cl, ++bool DirtyCardQueueSet::apply_closure_to_completed_buffer_helper(CardTableEntryClosure* cl, + uint worker_i, + BufferNode* nd) { + if (nd != NULL) { +@@ -259,7 +281,7 @@ void DirtyCardQueueSet::clear() { + } + _n_completed_buffers = 0; + _completed_buffers_tail = NULL; +- debug_only(assert_completed_buffer_list_len_correct_locked()); ++ DEBUG_ONLY(assert_completed_buffer_list_len_correct_locked()); + } + while (buffers_to_delete != NULL) { + BufferNode* nd = buffers_to_delete; +@@ -291,10 +313,11 @@ void DirtyCardQueueSet::concatenate_logs() { + for (JavaThread* t = Threads::first(); t; t = t->next()) { + DirtyCardQueue& dcq = t->dirty_card_queue(); + if (dcq.size() != 0) { +- void **buf = t->dirty_card_queue().get_buf(); ++ void **buf = dcq.get_buf(); + // We must NULL out the unused entries, then enqueue. +- for (size_t i = 0; i < t->dirty_card_queue().get_index(); i += oopSize) { +- buf[PtrQueue::byte_index_to_index((int)i)] = NULL; ++ size_t limit = dcq.byte_index_to_index(dcq.get_index()); ++ for (size_t i = 0; i < limit; ++i) { ++ buf[i] = NULL; + } + enqueue_complete_buffer(dcq.get_buf(), dcq.get_index()); + dcq.reinitialize(); +diff --git a/hotspot/src/share/vm/gc_implementation/g1/dirtyCardQueue.hpp b/hotspot/src/share/vm/gc_implementation/g1/dirtyCardQueue.hpp +index 986c0ea3c..250145937 100644 +--- a/hotspot/src/share/vm/gc_implementation/g1/dirtyCardQueue.hpp ++++ b/hotspot/src/share/vm/gc_implementation/g1/dirtyCardQueue.hpp +@@ -29,6 +29,7 @@ + #include "memory/allocation.hpp" + + class FreeIdSet; ++class DirtyCardQueueSet; + + // A closure class for processing card table entries. Note that we don't + // require these closure objects to be stack-allocated. +@@ -42,14 +43,11 @@ public: + // A ptrQueue whose elements are "oops", pointers to object heads. + class DirtyCardQueue: public PtrQueue { + public: +- DirtyCardQueue(PtrQueueSet* qset_, bool perm = false) : +- // Dirty card queues are always active, so we create them with their +- // active field set to true. +- PtrQueue(qset_, perm, true /* active */) { } ++ DirtyCardQueue(DirtyCardQueueSet* qset, bool permanent = false); + + // Flush before destroying; queue may be used to capture pending work while + // doing something else, with auto-flush on completion. +- ~DirtyCardQueue() { if (!is_permanent()) flush(); } ++ ~DirtyCardQueue(); + + // Process queue entries and release resources. + void flush() { flush_impl(); } +@@ -72,7 +70,6 @@ public: + bool consume = true, + uint worker_i = 0); + void **get_buf() { return _buf;} +- void set_buf(void **buf) {_buf = buf;} + size_t get_index() { return _index;} + void reinitialize() { _buf = 0; _sz = 0; _index = 0;} + }; +@@ -101,10 +98,13 @@ class DirtyCardQueueSet: public PtrQueueSet { + public: + DirtyCardQueueSet(bool notify_when_complete = true); + +- void initialize(CardTableEntryClosure* cl, Monitor* cbl_mon, Mutex* fl_lock, ++ void initialize(CardTableEntryClosure* cl, ++ Monitor* cbl_mon, ++ Mutex* fl_lock, + int process_completed_threshold, + int max_completed_queue, +- Mutex* lock, PtrQueueSet* fl_owner = NULL); ++ Mutex* lock, ++ DirtyCardQueueSet* fl_owner = NULL); + + // The number of parallel ids that can be claimed to allow collector or + // mutator threads to do card-processing work. +diff --git a/hotspot/src/share/vm/gc_implementation/g1/ptrQueue.cpp b/hotspot/src/share/vm/gc_implementation/g1/ptrQueue.cpp +index 2845d5186..c92b081c3 100644 +--- a/hotspot/src/share/vm/gc_implementation/g1/ptrQueue.cpp ++++ b/hotspot/src/share/vm/gc_implementation/g1/ptrQueue.cpp +@@ -30,24 +30,25 @@ + #include "runtime/mutexLocker.hpp" + #include "runtime/thread.inline.hpp" + +-PtrQueue::PtrQueue(PtrQueueSet* qset, bool perm, bool active) : ++PtrQueue::PtrQueue(PtrQueueSet* qset, bool permanent, bool active) : + _qset(qset), _buf(NULL), _index(0), _sz(0), _active(active), +- _perm(perm), _lock(NULL) ++ _permanent(permanent), _lock(NULL) + {} + + PtrQueue::~PtrQueue() { +- assert(_perm || (_buf == NULL), "queue must be flushed before delete"); ++ assert(_permanent || (_buf == NULL), "queue must be flushed before delete"); + } + + void PtrQueue::flush_impl() { +- if (!_perm && _buf != NULL) { ++ if (!_permanent && _buf != NULL) { + if (_index == _sz) { + // No work to do. + qset()->deallocate_buffer(_buf); + } else { + // We must NULL out the unused entries, then enqueue. +- for (size_t i = 0; i < _index; i += oopSize) { +- _buf[byte_index_to_index((int)i)] = NULL; ++ size_t limit = byte_index_to_index(_index); ++ for (size_t i = 0; i < limit; ++i) { ++ _buf[i] = NULL; + } + qset()->enqueue_complete_buffer(_buf); + } +@@ -66,8 +67,8 @@ void PtrQueue::enqueue_known_active(void* ptr) { + } + + assert(_index > 0, "postcondition"); +- _index -= oopSize; +- _buf[byte_index_to_index((int)_index)] = ptr; ++ _index -= sizeof(void*); ++ _buf[byte_index_to_index(_index)] = ptr; + assert(0 <= _index && _index <= _sz, "Invariant."); + } + +@@ -100,6 +101,26 @@ PtrQueueSet::PtrQueueSet(bool notify_when_complete) : + _fl_owner = this; + } + ++PtrQueueSet::~PtrQueueSet() { ++ // There are presently only a couple (derived) instances ever ++ // created, and they are permanent, so no harm currently done by ++ // doing nothing here. ++} ++ ++void PtrQueueSet::initialize(Monitor* cbl_mon, ++ Mutex* fl_lock, ++ int process_completed_threshold, ++ int max_completed_queue, ++ PtrQueueSet *fl_owner) { ++ _max_completed_queue = max_completed_queue; ++ _process_completed_threshold = process_completed_threshold; ++ _completed_queue_padding = 0; ++ assert(cbl_mon != NULL && fl_lock != NULL, "Init order issue?"); ++ _cbl_mon = cbl_mon; ++ _fl_lock = fl_lock; ++ _fl_owner = (fl_owner != NULL) ? fl_owner : this; ++} ++ + void** PtrQueueSet::allocate_buffer() { + assert(_sz > 0, "Didn't set a buffer size."); + MutexLockerEx x(_fl_owner->_fl_lock, Mutex::_no_safepoint_check_flag); +@@ -234,7 +255,7 @@ void PtrQueueSet::enqueue_complete_buffer(void** buf, size_t index) { + if (_notify_when_complete) + _cbl_mon->notify(); + } +- debug_only(assert_completed_buffer_list_len_correct_locked()); ++ DEBUG_ONLY(assert_completed_buffer_list_len_correct_locked()); + } + + int PtrQueueSet::completed_buffers_list_length() { +@@ -259,7 +280,7 @@ void PtrQueueSet::assert_completed_buffer_list_len_correct_locked() { + + void PtrQueueSet::set_buffer_size(size_t sz) { + assert(_sz == 0 && sz > 0, "Should be called only once."); +- _sz = sz * oopSize; ++ _sz = sz * sizeof(void*); + } + + // Merge lists of buffers. Notify the processing threads. +diff --git a/hotspot/src/share/vm/gc_implementation/g1/ptrQueue.hpp b/hotspot/src/share/vm/gc_implementation/g1/ptrQueue.hpp +index 27404f0a9..9c969b2ad 100644 +--- a/hotspot/src/share/vm/gc_implementation/g1/ptrQueue.hpp ++++ b/hotspot/src/share/vm/gc_implementation/g1/ptrQueue.hpp +@@ -40,44 +40,49 @@ class PtrQueueSet; + class PtrQueue VALUE_OBJ_CLASS_SPEC { + friend class VMStructs; + +-protected: ++ // Noncopyable - not defined. ++ PtrQueue(const PtrQueue&); ++ PtrQueue& operator=(const PtrQueue&); ++ + // The ptr queue set to which this queue belongs. +- PtrQueueSet* _qset; ++ PtrQueueSet* const _qset; + + // Whether updates should be logged. + bool _active; + ++ // If true, the queue is permanent, and doesn't need to deallocate ++ // its buffer in the destructor (since that obtains a lock which may not ++ // be legally locked by then. ++ const bool _permanent; ++ ++protected: + // The buffer. + void** _buf; +- // The index at which an object was last enqueued. Starts at "_sz" ++ // The (byte) index at which an object was last enqueued. Starts at "_sz" + // (indicating an empty buffer) and goes towards zero. + size_t _index; + +- // The size of the buffer. ++ // The (byte) size of the buffer. + size_t _sz; + +- // If true, the queue is permanent, and doesn't need to deallocate +- // its buffer in the destructor (since that obtains a lock which may not +- // be legally locked by then. +- bool _perm; +- + // If there is a lock associated with this buffer, this is that lock. + Mutex* _lock; + + PtrQueueSet* qset() { return _qset; } +- bool is_permanent() const { return _perm; } ++ bool is_permanent() const { return _permanent; } + + // Process queue entries and release resources, if not permanent. + void flush_impl(); + +-public: + // Initialize this queue to contain a null buffer, and be part of the + // given PtrQueueSet. +- PtrQueue(PtrQueueSet* qset, bool perm = false, bool active = false); ++ PtrQueue(PtrQueueSet* qset, bool permanent = false, bool active = false); + + // Requires queue flushed or permanent. + ~PtrQueue(); + ++public: ++ + // Associate a lock with a ptr queue. + void set_lock(Mutex* lock) { _lock = lock; } + +@@ -129,13 +134,9 @@ public: + + bool is_active() { return _active; } + +- static int byte_index_to_index(int ind) { +- assert((ind % oopSize) == 0, "Invariant."); +- return ind / oopSize; +- } +- +- static int index_to_byte_index(int byte_ind) { +- return byte_ind * oopSize; ++ static size_t byte_index_to_index(size_t ind) { ++ assert((ind % sizeof(void*)) == 0, "Invariant."); ++ return ind / sizeof(void*); + } + + // To support compiler. +@@ -246,26 +247,21 @@ protected: + return false; + } + +-public: + // Create an empty ptr queue set. + PtrQueueSet(bool notify_when_complete = false); ++ ~PtrQueueSet(); + + // Because of init-order concerns, we can't pass these as constructor + // arguments. +- void initialize(Monitor* cbl_mon, Mutex* fl_lock, ++ void initialize(Monitor* cbl_mon, ++ Mutex* fl_lock, + int process_completed_threshold, + int max_completed_queue, +- PtrQueueSet *fl_owner = NULL) { +- _max_completed_queue = max_completed_queue; +- _process_completed_threshold = process_completed_threshold; +- _completed_queue_padding = 0; +- assert(cbl_mon != NULL && fl_lock != NULL, "Init order issue?"); +- _cbl_mon = cbl_mon; +- _fl_lock = fl_lock; +- _fl_owner = (fl_owner != NULL) ? fl_owner : this; +- } ++ PtrQueueSet *fl_owner = NULL); ++ ++public: + +- // Return an empty oop array of size _sz (required to be non-zero). ++ // Return an empty array of size _sz (required to be non-zero). + void** allocate_buffer(); + + // Return an empty buffer to the free list. The "buf" argument is +diff --git a/hotspot/src/share/vm/gc_implementation/g1/satbQueue.cpp b/hotspot/src/share/vm/gc_implementation/g1/satbQueue.cpp +index d423c69ac..8c70b6795 100644 +--- a/hotspot/src/share/vm/gc_implementation/g1/satbQueue.cpp ++++ b/hotspot/src/share/vm/gc_implementation/g1/satbQueue.cpp +@@ -35,6 +35,15 @@ + + PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + ++ObjPtrQueue::ObjPtrQueue(SATBMarkQueueSet* qset, bool permanent) : ++ // SATB queues are only active during marking cycles. We create ++ // them with their active field set to false. If a thread is ++ // created during a cycle and its SATB queue needs to be activated ++ // before the thread starts running, we'll need to set its active ++ // field to true. This is done in JavaThread::initialize_queues(). ++ PtrQueue(qset, permanent, false /* active */) ++{ } ++ + void ObjPtrQueue::flush() { + // Filter now to possibly save work later. If filtering empties the + // buffer then flush_impl can deallocate the buffer. +@@ -101,7 +110,6 @@ inline bool requires_marking(const void* entry, G1CollectedHeap* heap) { + void ObjPtrQueue::filter() { + G1CollectedHeap* g1h = G1CollectedHeap::heap(); + void** buf = _buf; +- size_t sz = _sz; + + if (buf == NULL) { + // nothing to do +@@ -109,43 +117,37 @@ void ObjPtrQueue::filter() { + } + + // Used for sanity checking at the end of the loop. +- debug_only(size_t entries = 0; size_t retained = 0;) ++ DEBUG_ONLY(size_t entries = 0; size_t retained = 0;) + +- size_t i = sz; +- size_t new_index = sz; ++ assert(_index <= _sz, "invariant"); ++ void** limit = &buf[byte_index_to_index(_index)]; ++ void** src = &buf[byte_index_to_index(_sz)]; ++ void** dst = src; + +- while (i > _index) { +- assert(i > 0, "we should have at least one more entry to process"); +- i -= oopSize; +- debug_only(entries += 1;) +- void** p = &buf[byte_index_to_index((int) i)]; +- void* entry = *p; ++ while (limit < src) { ++ DEBUG_ONLY(entries += 1;) ++ --src; ++ void* entry = *src; + // NULL the entry so that unused parts of the buffer contain NULLs + // at the end. If we are going to retain it we will copy it to its + // final place. If we have retained all entries we have visited so + // far, we'll just end up copying it to the same place. +- *p = NULL; ++ *src = NULL; + + if (requires_marking(entry, g1h) && !g1h->isMarkedNext((oop)entry)) { +- assert(new_index > 0, "we should not have already filled up the buffer"); +- new_index -= oopSize; +- assert(new_index >= i, +- "new_index should never be below i, as we alwaysr compact 'up'"); +- void** new_p = &buf[byte_index_to_index((int) new_index)]; +- assert(new_p >= p, "the destination location should never be below " +- "the source as we always compact 'up'"); +- assert(*new_p == NULL, +- "we should have already cleared the destination location"); +- *new_p = entry; +- debug_only(retained += 1;) ++ --dst; ++ assert(*dst == NULL, "filtering destination should be clear"); ++ *dst = entry; ++ DEBUG_ONLY(retained += 1;); + } + } ++ size_t new_index = pointer_delta(dst, buf, 1); + + #ifdef ASSERT +- size_t entries_calc = (sz - _index) / oopSize; ++ size_t entries_calc = (_sz - _index) / sizeof(void*); + assert(entries == entries_calc, "the number of entries we counted " + "should match the number of entries we calculated"); +- size_t retained_calc = (sz - new_index) / oopSize; ++ size_t retained_calc = (_sz - new_index) / sizeof(void*); + assert(retained == retained_calc, "the number of retained entries we counted " + "should match the number of retained entries we calculated"); + #endif // ASSERT +@@ -172,11 +174,8 @@ bool ObjPtrQueue::should_enqueue_buffer() { + + filter(); + +- size_t sz = _sz; +- size_t all_entries = sz / oopSize; +- size_t retained_entries = (sz - _index) / oopSize; +- size_t perc = retained_entries * 100 / all_entries; +- bool should_enqueue = perc > (size_t) G1SATBBufferEnqueueingThresholdPercent; ++ size_t percent_used = ((_sz - _index) * 100) / _sz; ++ bool should_enqueue = percent_used > G1SATBBufferEnqueueingThresholdPercent; + return should_enqueue; + } + +@@ -187,8 +186,8 @@ void ObjPtrQueue::apply_closure_and_empty(SATBBufferClosure* cl) { + assert(_index % sizeof(void*) == 0, "invariant"); + assert(_sz % sizeof(void*) == 0, "invariant"); + assert(_index <= _sz, "invariant"); +- cl->do_buffer(_buf + byte_index_to_index((int)_index), +- byte_index_to_index((int)(_sz - _index))); ++ cl->do_buffer(_buf + byte_index_to_index(_index), ++ byte_index_to_index(_sz - _index)); + _index = _sz; + } + } +@@ -214,7 +213,7 @@ void ObjPtrQueue::print(const char* name, + + SATBMarkQueueSet::SATBMarkQueueSet() : + PtrQueueSet(), +- _shared_satb_queue(this, true /*perm*/) { } ++ _shared_satb_queue(this, true /* permanent */) { } + + void SATBMarkQueueSet::initialize(Monitor* cbl_mon, Mutex* fl_lock, + int process_completed_threshold, +@@ -301,7 +300,7 @@ bool SATBMarkQueueSet::apply_closure_to_completed_buffer(SATBBufferClosure* cl) + // Filtering can result in non-full completed buffers; see + // should_enqueue_buffer. + assert(_sz % sizeof(void*) == 0, "invariant"); +- size_t limit = ObjPtrQueue::byte_index_to_index((int)_sz); ++ size_t limit = ObjPtrQueue::byte_index_to_index(_sz); + for (size_t i = 0; i < limit; ++i) { + if (buf[i] != NULL) { + // Found the end of the block of NULLs; process the remainder. +diff --git a/hotspot/src/share/vm/gc_implementation/g1/satbQueue.hpp b/hotspot/src/share/vm/gc_implementation/g1/satbQueue.hpp +index 594895919..6651210a3 100644 +--- a/hotspot/src/share/vm/gc_implementation/g1/satbQueue.hpp ++++ b/hotspot/src/share/vm/gc_implementation/g1/satbQueue.hpp +@@ -50,13 +50,7 @@ private: + void filter(); + + public: +- ObjPtrQueue(PtrQueueSet* qset, bool perm = false) : +- // SATB queues are only active during marking cycles. We create +- // them with their active field set to false. If a thread is +- // created during a cycle and its SATB queue needs to be activated +- // before the thread starts running, we'll need to set its active +- // field to true. This is done in JavaThread::initialize_queues(). +- PtrQueue(qset, perm, false /* active */) { } ++ ObjPtrQueue(SATBMarkQueueSet* qset, bool permanent = false); + + // Process queue entries and free resources. + void flush(); +diff --git a/hotspot/test/gc/g1/TestPtrQueueSize.java b/hotspot/test/gc/g1/TestPtrQueueSize.java +new file mode 100644 +index 000000000..f46c95bec +--- /dev/null ++++ b/hotspot/test/gc/g1/TestPtrQueueSize.java +@@ -0,0 +1,58 @@ ++/* ++ * Copyright (c) 2023, Huawei Technologies Co., Ltd. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++ ++/** ++* @test TestPtrQueueSize.java ++* @key gc ++* @bug 6899049 ++* @summary Test size of PtrQueue; used by dirtyCardQueue and satbQueue ++* @library /testlibrary ++*/ ++ ++import com.oracle.java.testlibrary.ProcessTools; ++import com.oracle.java.testlibrary.OutputAnalyzer; ++ ++ public class TestPtrQueueSize { ++ public static void main(String[] args) throws Exception { ++ ++ ProcessBuilder pb = ProcessTools.createJavaProcessBuilder("-XX:+UseG1GC", ++ "-XX:G1SATBBufferSize=716M", ++ "-Xms1024m", ++ "-Xmx4096m", ++ SystemGCTest.class.getName()); ++ ++ OutputAnalyzer output = new OutputAnalyzer(pb.start()); ++ ++ System.out.println("Output:\n" + output.getOutput()); ++ ++ output.shouldHaveExitValue(0); ++ } ++ ++ static class SystemGCTest { ++ public static void main(String [] args) { ++ for (int i = 0; i < 500; ++i) { ++ byte[] bArray = new byte[1024*1024]; ++ } ++ } ++ } ++ } +\ No newline at end of file +-- +2.22.0 + diff --git a/add-8142508-To-bring-j.u.z.ZipFile-s-native-implemen.patch b/add-8142508-To-bring-j.u.z.ZipFile-s-native-implemen.patch new file mode 100644 index 0000000..0bbe954 --- /dev/null +++ b/add-8142508-To-bring-j.u.z.ZipFile-s-native-implemen.patch @@ -0,0 +1,2911 @@ +From 7590fffae62f5a4157ab7612c186b442c81d5682 Mon Sep 17 00:00:00 2001 +Date: Thu, 21 Sep 2023 15:15:39 +0800 +Subject: add 8142508-To-bring-j.u.z.ZipFile-s-native-implementati + +--- + jdk/make/mapfiles/libzip/mapfile-vers | 23 +- + jdk/make/mapfiles/libzip/reorder-sparc | 17 - + jdk/make/mapfiles/libzip/reorder-sparcv9 | 16 - + jdk/make/mapfiles/libzip/reorder-x86 | 19 - + .../share/classes/java/util/jar/JarFile.java | 5 +- + .../share/classes/java/util/zip/ZipCoder.java | 21 +- + .../share/classes/java/util/zip/ZipFile.java | 933 ++++++++++++++---- + .../share/classes/java/util/zip/ZipUtils.java | 79 +- + .../sun/misc/JavaUtilZipFileAccess.java | 1 + + jdk/src/share/classes/sun/misc/VM.java | 3 - + jdk/src/share/native/java/util/zip/ZipFile.c | 403 -------- + jdk/src/share/native/java/util/zip/zip_util.c | 29 +- + jdk/src/share/native/java/util/zip/zip_util.h | 1 - + jdk/test/java/nio/file/spi/TestProvider.java | 274 ++++- + .../java/util/zip/ZipFile/TestZipFile.java | 375 +++++++ + .../imageio/plugins/png/ItxtUtf8Test.java | 2 +- + 16 files changed, 1440 insertions(+), 761 deletions(-) + delete mode 100644 jdk/src/share/native/java/util/zip/ZipFile.c + create mode 100644 jdk/test/java/util/zip/ZipFile/TestZipFile.java + +diff --git a/jdk/make/mapfiles/libzip/mapfile-vers b/jdk/make/mapfiles/libzip/mapfile-vers +index 5d33990c3..e7394ae61 100644 +--- a/jdk/make/mapfiles/libzip/mapfile-vers ++++ b/jdk/make/mapfiles/libzip/mapfile-vers +@@ -1,5 +1,5 @@ + # +-# Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. ++# Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. + # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + # + # This code is free software; you can redistribute it and/or modify it +@@ -27,7 +27,6 @@ + + SUNWprivate_1.1 { + global: +- Java_java_util_jar_JarFile_getMetaInfEntryNames; + Java_java_util_zip_Adler32_update; + Java_java_util_zip_Adler32_updateBytes; + Java_java_util_zip_Adler32_updateByteBuffer; +@@ -48,26 +47,6 @@ SUNWprivate_1.1 { + Java_java_util_zip_Inflater_initIDs; + Java_java_util_zip_Inflater_reset; + Java_java_util_zip_Inflater_setDictionary; +- Java_java_util_zip_ZipFile_close; +- Java_java_util_zip_ZipFile_getCommentBytes; +- Java_java_util_zip_ZipFile_freeEntry; +- Java_java_util_zip_ZipFile_getEntry; +- Java_java_util_zip_ZipFile_getEntryBytes; +- Java_java_util_zip_ZipFile_getEntryCrc; +- Java_java_util_zip_ZipFile_getEntryCSize; +- Java_java_util_zip_ZipFile_getEntryFlag; +- Java_java_util_zip_ZipFile_getEntryMethod; +- Java_java_util_zip_ZipFile_getEntrySize; +- Java_java_util_zip_ZipFile_getEntryTime; +- Java_java_util_zip_ZipFile_getNextEntry; +- Java_java_util_zip_ZipFile_getZipMessage; +- Java_java_util_zip_ZipFile_getTotal; +- Java_java_util_zip_ZipFile_initIDs; +- Java_java_util_zip_ZipFile_open; +- Java_java_util_zip_ZipFile_read; +- Java_java_util_zip_ZipFile_startsWithLOC; +- Java_java_util_zip_ZipFile_getManifestNum; +- + ZIP_Close; + ZIP_CRC32; + ZIP_FindEntry; +diff --git a/jdk/make/mapfiles/libzip/reorder-sparc b/jdk/make/mapfiles/libzip/reorder-sparc +index 9d3aae88e..b1957dce7 100644 +--- a/jdk/make/mapfiles/libzip/reorder-sparc ++++ b/jdk/make/mapfiles/libzip/reorder-sparc +@@ -15,31 +15,14 @@ text: .text%ZIP_GetEntry; + text: .text%ZIP_Lock; + text: .text%ZIP_Unlock; + text: .text%ZIP_FreeEntry; +-text: .text%Java_java_util_zip_ZipFile_initIDs; +-text: .text%Java_java_util_zip_ZipFile_open; +-text: .text%Java_java_util_zip_ZipFile_getTotal; +-text: .text%Java_java_util_zip_ZipFile_startsWithLOC; +-text: .text%Java_java_util_zip_ZipFile_getManifestNum; +-text: .text%Java_java_util_zip_ZipFile_getEntry; +-text: .text%Java_java_util_zip_ZipFile_freeEntry; +-text: .text%Java_java_util_zip_ZipFile_getEntryTime; +-text: .text%Java_java_util_zip_ZipFile_getEntryCrc; +-text: .text%Java_java_util_zip_ZipFile_getEntryCSize; +-text: .text%Java_java_util_zip_ZipFile_getEntrySize; +-text: .text%Java_java_util_zip_ZipFile_getEntryFlag; +-text: .text%Java_java_util_zip_ZipFile_getEntryMethod; +-text: .text%Java_java_util_zip_ZipFile_getEntryBytes; + text: .text%Java_java_util_zip_Inflater_initIDs; + text: .text%Java_java_util_zip_Inflater_init; + text: .text%inflateInit2_; + text: .text%zcalloc; + text: .text%Java_java_util_zip_Inflater_inflateBytes; +-text: .text%Java_java_util_zip_ZipFile_read; + text: .text%ZIP_Read; + text: .text%zcfree; +-text: .text%Java_java_util_jar_JarFile_getMetaInfEntryNames; + text: .text%Java_java_util_zip_Inflater_reset; + text: .text%Java_java_util_zip_Inflater_end; + text: .text%inflateEnd; +-text: .text%Java_java_util_zip_ZipFile_close; + text: .text%ZIP_Close; +diff --git a/jdk/make/mapfiles/libzip/reorder-sparcv9 b/jdk/make/mapfiles/libzip/reorder-sparcv9 +index bf127e9cf..458d4c34f 100644 +--- a/jdk/make/mapfiles/libzip/reorder-sparcv9 ++++ b/jdk/make/mapfiles/libzip/reorder-sparcv9 +@@ -15,20 +15,6 @@ text: .text%ZIP_Lock; + text: .text%readLOC: OUTPUTDIR/zip_util.o; + text: .text%ZIP_Unlock; + text: .text%ZIP_FreeEntry; +-text: .text%Java_java_util_zip_ZipFile_initIDs; +-text: .text%Java_java_util_zip_ZipFile_open; +-text: .text%Java_java_util_zip_ZipFile_getTotal; +-text: .text%Java_java_util_zip_ZipFile_startsWithLOC; +-text: .text%Java_java_util_zip_ZipFile_getManifestNum; +-text: .text%Java_java_util_zip_ZipFile_getEntry; +-text: .text%Java_java_util_zip_ZipFile_freeEntry; +-text: .text%Java_java_util_zip_ZipFile_getEntryTime; +-text: .text%Java_java_util_zip_ZipFile_getEntryCrc; +-text: .text%Java_java_util_zip_ZipFile_getEntryCSize; +-text: .text%Java_java_util_zip_ZipFile_getEntrySize; +-text: .text%Java_java_util_zip_ZipFile_getEntryFlag; +-text: .text%Java_java_util_zip_ZipFile_getEntryMethod; +-text: .text%Java_java_util_zip_ZipFile_getEntryBytes; + text: .text%Java_java_util_zip_Inflater_initIDs; + text: .text%Java_java_util_zip_Inflater_init; + text: .text%inflateInit2_; +@@ -36,7 +22,6 @@ text: .text%zcalloc; + text: .text%inflateReset; + text: .text%Java_java_util_zip_Inflater_inflateBytes; + text: .text%inflate; +-text: .text%Java_java_util_zip_ZipFile_read; + text: .text%ZIP_Read; + text: .text%huft_build: OUTPUTDIR/inftrees.o; + text: .text%zcfree; +@@ -45,6 +30,5 @@ text: .text%ZIP_ReadEntry; + text: .text%InflateFully; + text: .text%inflateEnd; + text: .text%Java_java_util_zip_Inflater_reset; +-text: .text%Java_java_util_zip_ZipFile_close; + text: .text%ZIP_Close; + text: .text%Java_java_util_zip_Inflater_end; +diff --git a/jdk/make/mapfiles/libzip/reorder-x86 b/jdk/make/mapfiles/libzip/reorder-x86 +index d4e84c38c..62f11a990 100644 +--- a/jdk/make/mapfiles/libzip/reorder-x86 ++++ b/jdk/make/mapfiles/libzip/reorder-x86 +@@ -16,36 +16,17 @@ text: .text%ZIP_Lock; + text: .text%readLOC: OUTPUTDIR/zip_util.o; + text: .text%ZIP_Unlock; + text: .text%ZIP_FreeEntry; +-text: .text%Java_java_util_zip_ZipFile_initIDs; +-text: .text%Java_java_util_zip_ZipFile_open; +-text: .text%Java_java_util_zip_ZipFile_getTotal; +-text: .text%Java_java_util_zip_ZipFile_startsWithLOC; +-text: .text%Java_java_util_zip_ZipFile_getManifestNum; +-text: .text%Java_java_util_zip_ZipFile_getEntry; +-text: .text%Java_java_util_zip_ZipFile_freeEntry; +-text: .text%Java_java_util_zip_ZipFile_getEntryTime; +-text: .text%Java_java_util_zip_ZipFile_getEntryCrc; +-text: .text%Java_java_util_zip_ZipFile_getEntryCSize; +-text: .text%Java_java_util_zip_ZipFile_getEntrySize; +-text: .text%Java_java_util_zip_ZipFile_getEntryFlag; +-text: .text%Java_java_util_zip_ZipFile_getEntryMethod; +-text: .text%Java_java_util_zip_ZipFile_getEntryBytes; +-text: .text%Java_java_util_zip_Inflater_initIDs; +-text: .text%Java_java_util_zip_Inflater_init; + text: .text%inflateInit2_; + text: .text%zcalloc; + text: .text%inflateReset; + text: .text%Java_java_util_zip_Inflater_inflateBytes; + text: .text%inflate; +-text: .text%Java_java_util_zip_ZipFile_read; + text: .text%ZIP_Read; + text: .text%huft_build: OUTPUTDIR/inftrees.o; + text: .text%zcfree; +-text: .text%Java_java_util_jar_JarFile_getMetaInfEntryNames; + text: .text%ZIP_ReadEntry; + text: .text%InflateFully; + text: .text%inflateEnd; + text: .text%Java_java_util_zip_Inflater_reset; +-text: .text%Java_java_util_zip_ZipFile_close; + text: .text%ZIP_Close; + text: .text%Java_java_util_zip_Inflater_end; +diff --git a/jdk/src/share/classes/java/util/jar/JarFile.java b/jdk/src/share/classes/java/util/jar/JarFile.java +index a26dcc4a1..2878e175e 100644 +--- a/jdk/src/share/classes/java/util/jar/JarFile.java ++++ b/jdk/src/share/classes/java/util/jar/JarFile.java +@@ -213,7 +213,10 @@ class JarFile extends ZipFile { + return man; + } + +- private native String[] getMetaInfEntryNames(); ++ private String[] getMetaInfEntryNames() { ++ return sun.misc.SharedSecrets.getJavaUtilZipFileAccess() ++ .getMetaInfEntryNames((ZipFile)this); ++ } + + /** + * Returns the JarEntry for the given entry name or +diff --git a/jdk/src/share/classes/java/util/zip/ZipCoder.java b/jdk/src/share/classes/java/util/zip/ZipCoder.java +index b920b820e..243d6e8c0 100644 +--- a/jdk/src/share/classes/java/util/zip/ZipCoder.java ++++ b/jdk/src/share/classes/java/util/zip/ZipCoder.java +@@ -43,7 +43,7 @@ import sun.nio.cs.ArrayEncoder; + + final class ZipCoder { + +- String toString(byte[] ba, int length) { ++ String toString(byte[] ba, int off, int length) { + CharsetDecoder cd = decoder().reset(); + int len = (int)(length * cd.maxCharsPerByte()); + char[] ca = new char[len]; +@@ -53,12 +53,12 @@ final class ZipCoder { + // CodingErrorAction.REPLACE mode. ZipCoder uses + // REPORT mode. + if (isUTF8 && cd instanceof ArrayDecoder) { +- int clen = ((ArrayDecoder)cd).decode(ba, 0, length, ca); ++ int clen = ((ArrayDecoder)cd).decode(ba, off, length, ca); + if (clen == -1) // malformed + throw new IllegalArgumentException("MALFORMED"); + return new String(ca, 0, clen); + } +- ByteBuffer bb = ByteBuffer.wrap(ba, 0, length); ++ ByteBuffer bb = ByteBuffer.wrap(ba, off, length); + CharBuffer cb = CharBuffer.wrap(ca); + CoderResult cr = cd.decode(bb, cb, true); + if (!cr.isUnderflow()) +@@ -69,8 +69,12 @@ final class ZipCoder { + return new String(ca, 0, cb.position()); + } + ++ String toString(byte[] ba, int length) { ++ return toString(ba, 0, length); ++ } ++ + String toString(byte[] ba) { +- return toString(ba, ba.length); ++ return toString(ba, 0, ba.length); + } + + byte[] getBytes(String s) { +@@ -111,13 +115,16 @@ final class ZipCoder { + return utf8.getBytes(s); + } + +- + String toStringUTF8(byte[] ba, int len) { ++ return toStringUTF8(ba, 0, len); ++ } ++ ++ String toStringUTF8(byte[] ba, int off, int len) { + if (isUTF8) +- return toString(ba, len); ++ return toString(ba, off, len); + if (utf8 == null) + utf8 = new ZipCoder(StandardCharsets.UTF_8); +- return utf8.toString(ba, len); ++ return utf8.toString(ba, off, len); + } + + boolean isUTF8() { +diff --git a/jdk/src/share/classes/java/util/zip/ZipFile.java b/jdk/src/share/classes/java/util/zip/ZipFile.java +index 9f8aff6ef..5d9b0de97 100644 +--- a/jdk/src/share/classes/java/util/zip/ZipFile.java ++++ b/jdk/src/share/classes/java/util/zip/ZipFile.java +@@ -30,14 +30,24 @@ import java.io.InputStream; + import java.io.IOException; + import java.io.EOFException; + import java.io.File; ++import java.io.FileNotFoundException; ++import java.io.RandomAccessFile; + import java.nio.charset.Charset; + import java.nio.charset.StandardCharsets; ++import java.nio.file.attribute.BasicFileAttributes; ++import java.nio.file.Path; ++import java.nio.file.Files; ++import java.nio.file.NoSuchFileException; ++ + import java.util.ArrayDeque; ++import java.util.ArrayList; ++import java.util.Arrays; + import java.util.Deque; + import java.util.Enumeration; + import java.util.HashMap; + import java.util.Iterator; + import java.util.Map; ++import java.util.Objects; + import java.util.NoSuchElementException; + import java.util.Spliterator; + import java.util.Spliterators; +@@ -46,7 +56,9 @@ import java.util.jar.JarFile; + import java.util.stream.Stream; + import java.util.stream.StreamSupport; + ++import static java.util.zip.ZipConstants.*; + import static java.util.zip.ZipConstants64.*; ++import static java.util.zip.ZipUtils.*; + + /** + * This class is used to read entries from a zip file. +@@ -59,12 +71,11 @@ import static java.util.zip.ZipConstants64.*; + */ + public + class ZipFile implements ZipConstants, Closeable { +- private long jzfile; // address of jzfile data ++ + private final String name; // zip file name +- private final int total; // total number of entries +- private final boolean locsig; // if zip file starts with LOCSIG (usually true) + private volatile boolean closeRequested = false; +- private int manifestNum = 0; // number of META-INF/MANIFEST.MF, case insensitive ++ private Source zsrc; ++ private ZipCoder zc; + + private static final int STORED = ZipEntry.STORED; + private static final int DEFLATED = ZipEntry.DEFLATED; +@@ -83,26 +94,11 @@ class ZipFile implements ZipConstants, Closeable { + */ + public static final int OPEN_DELETE = 0x4; + +- static { +- /* Zip library is loaded from System.initializeSystemClass */ +- initIDs(); +- } +- +- private static native void initIDs(); +- +- private static final boolean usemmap; +- + private static final boolean ensuretrailingslash; + + static { +- // A system prpperty to disable mmap use to avoid vm crash when +- // in-use zip file is accidently overwritten by others. +- String prop = sun.misc.VM.getSavedProperty("sun.zip.disableMemoryMapping"); +- usemmap = (prop == null || +- !(prop.length() == 0 || prop.equalsIgnoreCase("true"))); +- + // see getEntry() for details +- prop = sun.misc.VM.getSavedProperty("jdk.util.zip.ensureTrailingSlash"); ++ String prop = sun.misc.VM.getSavedProperty("jdk.util.zip.ensureTrailingSlash"); + ensuretrailingslash = prop == null || !prop.equalsIgnoreCase("false"); + } + +@@ -171,8 +167,6 @@ class ZipFile implements ZipConstants, Closeable { + this(file, OPEN_READ); + } + +- private ZipCoder zc; +- + /** + * Opens a new ZipFile to read from the specified + * File object in the specified mode. The mode argument +@@ -224,17 +218,13 @@ class ZipFile implements ZipConstants, Closeable { + sm.checkDelete(name); + } + } +- if (charset == null) +- throw new NullPointerException("charset is null"); ++ Objects.requireNonNull(charset, "charset"); + this.zc = ZipCoder.get(charset); ++ this.name = name; + long t0 = System.nanoTime(); +- jzfile = open(name, mode, file.lastModified(), usemmap); ++ this.zsrc = Source.get(file, (mode & OPEN_DELETE) != 0); + sun.misc.PerfCounter.getZipFileOpenTime().addElapsedTimeFrom(t0); + sun.misc.PerfCounter.getZipFileCount().increment(); +- this.name = name; +- this.total = getTotal(jzfile); +- this.locsig = startsWithLOC(jzfile); +- this.manifestNum = getManifestNum(jzfile); + } + + /** +@@ -268,6 +258,7 @@ class ZipFile implements ZipConstants, Closeable { + + /** + * Opens a ZIP file for reading given the specified File object. ++ * + * @param file the ZIP file to be opened for reading + * @param charset + * The {@linkplain java.nio.charset.Charset charset} to be +@@ -298,10 +289,10 @@ class ZipFile implements ZipConstants, Closeable { + public String getComment() { + synchronized (this) { + ensureOpen(); +- byte[] bcomm = getCommentBytes(jzfile); +- if (bcomm == null) ++ if (zsrc.comment == null) { + return null; +- return zc.toString(bcomm, bcomm.length); ++ } ++ return zc.toString(zsrc.comment); + } + } + +@@ -314,47 +305,29 @@ class ZipFile implements ZipConstants, Closeable { + * @throws IllegalStateException if the zip file has been closed + */ + public ZipEntry getEntry(String name) { +- if (name == null) { +- throw new NullPointerException("name"); +- } +- long jzentry = 0; ++ ++ Objects.requireNonNull(name, "name"); + synchronized (this) { + ensureOpen(); +- jzentry = getEntry(jzfile, zc.getBytes(name), true); +- if (jzentry != 0) { +- // If no entry is found for the specified 'name' and +- // the 'name' does not end with a forward slash '/', +- // the implementation tries to find the entry with a +- // slash '/' appended to the end of the 'name', before +- // returning null. When such entry is found, the name +- // that actually is found (with a slash '/' attached) +- // is used +- // (disabled if jdk.util.zip.ensureTrailingSlash=false) +- ZipEntry ze = ensuretrailingslash ? getZipEntry(null, jzentry) +- : getZipEntry(name, jzentry); +- freeEntry(jzfile, jzentry); +- return ze; ++ int pos = zsrc.getEntryPos(zc.getBytes(name), true); ++ if (pos != -1) { ++ return ensuretrailingslash ? getZipEntry(null, pos) ++ : getZipEntry(name, pos); + } + } + return null; + } + +- private static native long getEntry(long jzfile, byte[] name, +- boolean addSlash); +- +- // freeEntry releases the C jzentry struct. +- private static native void freeEntry(long jzfile, long jzentry); +- +- // the outstanding inputstreams that need to be closed, ++ // The outstanding inputstreams that need to be closed, + // mapped to the inflater objects they use. + private final Map streams = new WeakHashMap<>(); + + /** + * Returns an input stream for reading the contents of the specified + * zip file entry. +- * +- *

Closing this ZIP file will, in turn, close all input +- * streams that have been returned by invocations of this method. ++ *

++ * Closing this ZIP file will, in turn, close all input streams that ++ * have been returned by invocations of this method. + * + * @param entry the zip file entry + * @return the input stream for reading the contents of the specified +@@ -364,37 +337,38 @@ class ZipFile implements ZipConstants, Closeable { + * @throws IllegalStateException if the zip file has been closed + */ + public InputStream getInputStream(ZipEntry entry) throws IOException { +- if (entry == null) { +- throw new NullPointerException("entry"); +- } +- long jzentry = 0; ++ Objects.requireNonNull(entry, "entry"); ++ int pos = -1; + ZipFileInputStream in = null; + synchronized (this) { + ensureOpen(); + if (!zc.isUTF8() && (entry.flag & EFS) != 0) { +- jzentry = getEntry(jzfile, zc.getBytesUTF8(entry.name), false); ++ pos = zsrc.getEntryPos(zc.getBytesUTF8(entry.name), false); + } else { +- jzentry = getEntry(jzfile, zc.getBytes(entry.name), false); ++ pos = zsrc.getEntryPos(zc.getBytes(entry.name), false); + } +- if (jzentry == 0) { ++ if (pos == -1) { + return null; + } +- in = new ZipFileInputStream(jzentry); +- +- switch (getEntryMethod(jzentry)) { ++ in = new ZipFileInputStream(zsrc.cen, pos); ++ switch (CENHOW(zsrc.cen, pos)) { + case STORED: + synchronized (streams) { + streams.put(in, null); + } + return in; + case DEFLATED: ++ // Inflater likes a bit of slack + // MORE: Compute good size for inflater stream: +- long size = getEntrySize(jzentry) + 2; // Inflater likes a bit of slack +- if (size > 65536) size = 8192; +- if (size <= 0) size = 4096; ++ long size = CENLEN(zsrc.cen, pos) + 2; ++ if (size > 65536) { ++ size = 8192; ++ } ++ if (size <= 0) { ++ size = 4096; ++ } + Inflater inf = getInflater(); +- InputStream is = +- new ZipFileInflaterInputStream(in, inf, (int)size); ++ InputStream is = new ZipFileInflaterInputStream(in, inf, (int)size); + synchronized (streams) { + streams.put(is, inf); + } +@@ -467,8 +441,8 @@ class ZipFile implements ZipConstants, Closeable { + private Inflater getInflater() { + Inflater inf; + synchronized (inflaterCache) { +- while (null != (inf = inflaterCache.poll())) { +- if (false == inf.ended()) { ++ while ((inf = inflaterCache.poll()) != null) { ++ if (!inf.ended()) { + return inf; + } + } +@@ -480,7 +454,7 @@ class ZipFile implements ZipConstants, Closeable { + * Releases the specified inflater to the list of available inflaters. + */ + private void releaseInflater(Inflater inf) { +- if (false == inf.ended()) { ++ if (!inf.ended()) { + inf.reset(); + synchronized (inflaterCache) { + inflaterCache.add(inf); +@@ -489,7 +463,7 @@ class ZipFile implements ZipConstants, Closeable { + } + + // List of available Inflater objects for decompression +- private Deque inflaterCache = new ArrayDeque<>(); ++ private final Deque inflaterCache = new ArrayDeque<>(); + + /** + * Returns the path name of the ZIP file. +@@ -501,9 +475,13 @@ class ZipFile implements ZipConstants, Closeable { + + private class ZipEntryIterator implements Enumeration, Iterator { + private int i = 0; ++ private final int entryCount; + + public ZipEntryIterator() { +- ensureOpen(); ++ synchronized (ZipFile.this) { ++ ensureOpen(); ++ this.entryCount = zsrc.total; ++ } + } + + public boolean hasMoreElements() { +@@ -511,10 +489,7 @@ class ZipFile implements ZipConstants, Closeable { + } + + public boolean hasNext() { +- synchronized (ZipFile.this) { +- ensureOpen(); +- return i < total; +- } ++ return i < entryCount; + } + + public ZipEntry nextElement() { +@@ -524,28 +499,11 @@ class ZipFile implements ZipConstants, Closeable { + public ZipEntry next() { + synchronized (ZipFile.this) { + ensureOpen(); +- if (i >= total) { ++ if (!hasNext()) { + throw new NoSuchElementException(); + } +- long jzentry = getNextEntry(jzfile, i++); +- if (jzentry == 0) { +- String message; +- if (closeRequested) { +- message = "ZipFile concurrently closed"; +- } else { +- message = getZipMessage(ZipFile.this.jzfile); +- } +- throw new ZipError("jzentry == 0" + +- ",\n jzfile = " + ZipFile.this.jzfile + +- ",\n total = " + ZipFile.this.total + +- ",\n name = " + ZipFile.this.name + +- ",\n i = " + i + +- ",\n message = " + message +- ); +- } +- ZipEntry ze = getZipEntry(null, jzentry); +- freeEntry(jzfile, jzentry); +- return ze; ++ // each "entry" has 3 ints in table entries ++ return getZipEntry(null, zsrc.getEntryPos(i++ * 3)); + } + } + } +@@ -575,50 +533,53 @@ class ZipFile implements ZipConstants, Closeable { + Spliterator.IMMUTABLE | Spliterator.NONNULL), false); + } + +- private ZipEntry getZipEntry(String name, long jzentry) { +- ZipEntry e = new ZipEntry(); +- e.flag = getEntryFlag(jzentry); // get the flag first +- if (name != null) { +- e.name = name; +- } else { +- byte[] bname = getEntryBytes(jzentry, JZENTRY_NAME); +- if (bname == null) { +- e.name = ""; // length 0 empty name +- } else if (!zc.isUTF8() && (e.flag & EFS) != 0) { +- e.name = zc.toStringUTF8(bname, bname.length); ++ /* Checks ensureOpen() before invoke this method */ ++ private ZipEntry getZipEntry(String name, int pos) { ++ byte[] cen = zsrc.cen; ++ int nlen = CENNAM(cen, pos); ++ int elen = CENEXT(cen, pos); ++ int clen = CENCOM(cen, pos); ++ int flag = CENFLG(cen, pos); ++ if (name == null) { ++ if (!zc.isUTF8() && (flag & EFS) != 0) { ++ name = zc.toStringUTF8(cen, pos + CENHDR, nlen); + } else { +- e.name = zc.toString(bname, bname.length); +- } +- } +- e.xdostime = getEntryTime(jzentry); +- e.crc = getEntryCrc(jzentry); +- e.size = getEntrySize(jzentry); +- e.csize = getEntryCSize(jzentry); +- e.method = getEntryMethod(jzentry); +- e.setExtra0(getEntryBytes(jzentry, JZENTRY_EXTRA), false); +- byte[] bcomm = getEntryBytes(jzentry, JZENTRY_COMMENT); +- if (bcomm == null) { +- e.comment = null; +- } else { +- if (!zc.isUTF8() && (e.flag & EFS) != 0) { +- e.comment = zc.toStringUTF8(bcomm, bcomm.length); ++ name = zc.toString(cen, pos + CENHDR, nlen); ++ } ++ } ++ ZipEntry e = new ZipEntry(name); ++ e.flag = flag; ++ e.xdostime = CENTIM(cen, pos); ++ e.crc = CENCRC(cen, pos); ++ e.size = CENLEN(cen, pos); ++ e.csize = CENSIZ(cen, pos); ++ e.method = CENHOW(cen, pos); ++ if (elen != 0) { ++ int start = pos + CENHDR + nlen; ++ e.setExtra0(Arrays.copyOfRange(cen, start, start + elen), true); ++ } ++ if (clen != 0) { ++ int start = pos + CENHDR + nlen + elen; ++ if (!zc.isUTF8() && (flag & EFS) != 0) { ++ e.comment = zc.toStringUTF8(cen, start, clen); + } else { +- e.comment = zc.toString(bcomm, bcomm.length); ++ e.comment = zc.toString(cen, start, clen); + } + } + return e; + } + +- private static native long getNextEntry(long jzfile, int i); +- + /** + * Returns the number of entries in the ZIP file. ++ * + * @return the number of entries in the ZIP file + * @throws IllegalStateException if the zip file has been closed + */ + public int size() { +- ensureOpen(); +- return total; ++ synchronized (this) { ++ ensureOpen(); ++ return zsrc.total; ++ } + } + + /** +@@ -630,14 +591,15 @@ class ZipFile implements ZipConstants, Closeable { + * @throws IOException if an I/O error has occurred + */ + public void close() throws IOException { +- if (closeRequested) ++ if (closeRequested) { + return; ++ } + closeRequested = true; + + synchronized (this) { + // Close streams, release their inflaters + synchronized (streams) { +- if (false == streams.isEmpty()) { ++ if (!streams.isEmpty()) { + Map copy = new HashMap<>(streams); + streams.clear(); + for (Map.Entry e : copy.entrySet()) { +@@ -649,21 +611,17 @@ class ZipFile implements ZipConstants, Closeable { + } + } + } +- + // Release cached inflaters +- Inflater inf; + synchronized (inflaterCache) { +- while (null != (inf = inflaterCache.poll())) { ++ Inflater inf; ++ while ((inf = inflaterCache.poll()) != null) { + inf.end(); + } + } +- +- if (jzfile != 0) { +- // Close the zip file +- long zf = this.jzfile; +- jzfile = 0; +- +- close(zf); ++ // Release zip src ++ if (zsrc != null) { ++ Source.close(zsrc); ++ zsrc = null; + } + } + } +@@ -686,14 +644,11 @@ class ZipFile implements ZipConstants, Closeable { + close(); + } + +- private static native void close(long jzfile); +- + private void ensureOpen() { + if (closeRequested) { + throw new IllegalStateException("zip file closed"); + } +- +- if (jzfile == 0) { ++ if (zsrc == null) { + throw new IllegalStateException("The object is not initialized."); + } + } +@@ -709,40 +664,99 @@ class ZipFile implements ZipConstants, Closeable { + * (possibly compressed) zip file entry. + */ + private class ZipFileInputStream extends InputStream { +- private volatile boolean zfisCloseRequested = false; +- protected long jzentry; // address of jzentry data ++ private volatile boolean closeRequested = false; + private long pos; // current position within entry data + protected long rem; // number of remaining bytes within entry + protected long size; // uncompressed size of this entry + +- ZipFileInputStream(long jzentry) { +- pos = 0; +- rem = getEntryCSize(jzentry); +- size = getEntrySize(jzentry); +- this.jzentry = jzentry; ++ ZipFileInputStream(byte[] cen, int cenpos) throws IOException { ++ rem = CENSIZ(cen, cenpos); ++ size = CENLEN(cen, cenpos); ++ pos = CENOFF(cen, cenpos); ++ // zip64 ++ if (rem == ZIP64_MAGICVAL || size == ZIP64_MAGICVAL || ++ pos == ZIP64_MAGICVAL) { ++ checkZIP64(cen, cenpos); ++ } ++ // negative for lazy initialization, see getDataOffset(); ++ pos = - (pos + ZipFile.this.zsrc.locpos); ++ } ++ ++ private void checkZIP64(byte[] cen, int cenpos) throws IOException { ++ int off = cenpos + CENHDR + CENNAM(cen, cenpos); ++ int end = off + CENEXT(cen, cenpos); ++ while (off + 4 < end) { ++ int tag = get16(cen, off); ++ int sz = get16(cen, off + 2); ++ off += 4; ++ if (off + sz > end) // invalid data ++ break; ++ if (tag == EXTID_ZIP64) { ++ if (size == ZIP64_MAGICVAL) { ++ if (sz < 8 || (off + 8) > end) ++ break; ++ size = get64(cen, off); ++ sz -= 8; ++ off += 8; ++ } ++ if (rem == ZIP64_MAGICVAL) { ++ if (sz < 8 || (off + 8) > end) ++ break; ++ rem = get64(cen, off); ++ sz -= 8; ++ off += 8; ++ } ++ if (pos == ZIP64_MAGICVAL) { ++ if (sz < 8 || (off + 8) > end) ++ break; ++ pos = get64(cen, off); ++ sz -= 8; ++ off += 8; ++ } ++ break; ++ } ++ off += sz; ++ } ++ } ++ ++ /* The Zip file spec explicitly allows the LOC extra data size to ++ * be different from the CEN extra data size. Since we cannot trust ++ * the CEN extra data size, we need to read the LOC to determine ++ * the entry data offset. ++ */ ++ private long initDataOffset() throws IOException { ++ if (pos <= 0) { ++ byte[] loc = new byte[LOCHDR]; ++ pos = -pos; ++ int len = ZipFile.this.zsrc.readFullyAt(loc, 0, loc.length, pos); ++ if (len != LOCHDR) { ++ throw new ZipException("ZipFile error reading zip file"); ++ } ++ if (LOCSIG(loc) != LOCSIG) { ++ throw new ZipException("ZipFile invalid LOC header (bad signature)"); ++ } ++ pos += LOCHDR + LOCNAM(loc) + LOCEXT(loc); ++ } ++ return pos; + } + + public int read(byte b[], int off, int len) throws IOException { + synchronized (ZipFile.this) { +- long rem = this.rem; +- long pos = this.pos; ++ ensureOpenOrZipException(); ++ initDataOffset(); + if (rem == 0) { + return -1; + } +- if (len <= 0) { +- return 0; +- } + if (len > rem) { + len = (int) rem; + } +- +- // Check if ZipFile open +- ensureOpenOrZipException(); +- len = ZipFile.read(ZipFile.this.jzfile, jzentry, pos, b, +- off, len); ++ if (len <= 0) { ++ return 0; ++ } ++ len = ZipFile.this.zsrc.readAt(b, off, len, pos); + if (len > 0) { +- this.pos = (pos + len); +- this.rem = (rem - len); ++ pos += len; ++ rem -= len; + } + } + if (rem == 0) { +@@ -760,11 +774,16 @@ class ZipFile implements ZipConstants, Closeable { + } + } + +- public long skip(long n) { +- if (n > rem) +- n = rem; +- pos += n; +- rem -= n; ++ public long skip(long n) throws IOException { ++ synchronized (ZipFile.this) { ++ ensureOpenOrZipException(); ++ initDataOffset(); ++ if (n > rem) { ++ n = rem; ++ } ++ pos += n; ++ rem -= n; ++ } + if (rem == 0) { + close(); + } +@@ -780,17 +799,11 @@ class ZipFile implements ZipConstants, Closeable { + } + + public void close() { +- if (zfisCloseRequested) ++ if (closeRequested) { + return; +- zfisCloseRequested = true; +- +- rem = 0; +- synchronized (ZipFile.this) { +- if (jzentry != 0 && ZipFile.this.jzfile != 0) { +- freeEntry(ZipFile.this.jzfile, jzentry); +- jzentry = 0; +- } + } ++ closeRequested = true; ++ rem = 0; + synchronized (streams) { + streams.remove(this); + } +@@ -805,7 +818,10 @@ class ZipFile implements ZipConstants, Closeable { + sun.misc.SharedSecrets.setJavaUtilZipFileAccess( + new sun.misc.JavaUtilZipFileAccess() { + public boolean startsWithLocHeader(ZipFile zip) { +- return zip.startsWithLocHeader(); ++ return zip.zsrc.startsWithLoc; ++ } ++ public String[] getMetaInfEntryNames(ZipFile zip) { ++ return zip.getMetaInfEntryNames(); + } + public int getManifestNum(JarFile jar) { + return ((ZipFile)jar).getManifestNum(); +@@ -815,11 +831,27 @@ class ZipFile implements ZipConstants, Closeable { + } + + /** +- * Returns {@code true} if, and only if, the zip file begins with {@code +- * LOCSIG}. ++ * Returns an array of strings representing the names of all entries ++ * that begin with "META-INF/" (case ignored). This method is used ++ * in JarFile, via SharedSecrets, as an optimization when looking up ++ * manifest and signature file entries. Returns null if no entries ++ * were found. + */ +- private boolean startsWithLocHeader() { +- return locsig; ++ private String[] getMetaInfEntryNames() { ++ synchronized (this) { ++ ensureOpen(); ++ if (zsrc.metanames == null) { ++ return null; ++ } ++ String[] names = new String[zsrc.metanames.length]; ++ byte[] cen = zsrc.cen; ++ for (int i = 0; i < names.length; i++) { ++ int pos = zsrc.metanames[i]; ++ names[i] = new String(cen, pos + CENHDR, CENNAM(cen, pos), ++ StandardCharsets.UTF_8); ++ } ++ return names; ++ } + } + + /* +@@ -830,31 +862,506 @@ class ZipFile implements ZipConstants, Closeable { + private int getManifestNum() { + synchronized (this) { + ensureOpen(); +- return manifestNum; ++ return zsrc.manifestNum; + } + } ++ ++ private static class Source { ++ // "META-INF/".length() ++ private static final int META_INF_LEN = 9; ++ private final Key key; // the key in files ++ private int refs = 1; ++ ++ private RandomAccessFile zfile; // zfile of the underlying zip file ++ private byte[] cen; // CEN & ENDHDR ++ private long locpos; // position of first LOC header (usually 0) ++ private byte[] comment; // zip file comment ++ // list of meta entries in META-INF dir ++ private int[] metanames; ++ private int manifestNum = 0; // number of META-INF/MANIFEST.MF, case insensitive ++ private final boolean startsWithLoc; // true, if zip file starts with LOCSIG (usually true) ++ ++ // A Hashmap for all entries. ++ // ++ // A cen entry of Zip/JAR file. As we have one for every entry in every active Zip/JAR, ++ // We might have a lot of these in a typical system. In order to save space we don't ++ // keep the name in memory, but merely remember a 32 bit {@code hash} value of the ++ // entry name and its offset {@code pos} in the central directory hdeader. ++ // ++ // private static class Entry { ++ // int hash; // 32 bit hashcode on name ++ // int next; // hash chain: index into entries ++ // int pos; // Offset of central directory file header ++ // } ++ // private Entry[] entries; // array of hashed cen entry ++ // ++ // To reduce the total size of entries further, we use a int[] here to store 3 "int" ++ // {@code hash}, {@code next and {@code "pos for each entry. The entry can then be ++ // referred by their index of their positions in the {@code entries}. ++ // ++ private int[] entries; // array of hashed cen entry ++ private int addEntry(int index, int hash, int next, int pos) { ++ entries[index++] = hash; ++ entries[index++] = next; ++ entries[index++] = pos; ++ return index; ++ } ++ private int getEntryHash(int index) { return entries[index]; } ++ private int getEntryNext(int index) { return entries[index + 1]; } ++ private int getEntryPos(int index) { return entries[index + 2]; } ++ private static final int ZIP_ENDCHAIN = -1; ++ private int total; // total number of entries ++ private int[] table; // Hash chain heads: indexes into entries ++ private int tablelen; // number of hash heads ++ ++ private static class Key { ++ BasicFileAttributes attrs; ++ File file; ++ ++ public Key(File file, BasicFileAttributes attrs) { ++ this.attrs = attrs; ++ this.file = file; ++ } ++ ++ public int hashCode() { ++ long t = attrs.lastModifiedTime().toMillis(); ++ return ((int)(t ^ (t >>> 32))) + file.hashCode(); ++ } ++ ++ public boolean equals(Object obj) { ++ if (obj instanceof Key) { ++ Key key = (Key)obj; ++ if (!attrs.lastModifiedTime().equals(key.attrs.lastModifiedTime())) { ++ return false; ++ } ++ Object fk = attrs.fileKey(); ++ if (fk != null) { ++ return fk.equals(key.attrs.fileKey()); ++ } else { ++ return file.equals(key.file); ++ } ++ } ++ return false; ++ } ++ } ++ private static final HashMap files = new HashMap<>(); ++ ++ ++ public static Source get(File file, boolean toDelete) throws IOException { ++ Key key = null; ++ try { ++ key = new Key(file, ++ Files.readAttributes(file.toPath(), BasicFileAttributes.class)); ++ } catch (NoSuchFileException exception) { ++ throw new FileNotFoundException(exception.getMessage()); ++ } ++ Source src = null; ++ synchronized (files) { ++ src = files.get(key); ++ if (src != null) { ++ src.refs++; ++ return src; ++ } ++ } ++ src = new Source(key, toDelete); ++ ++ synchronized (files) { ++ if (files.containsKey(key)) { // someone else put in first ++ src.close(); // close the newly created one ++ src = files.get(key); ++ src.refs++; ++ return src; ++ } ++ files.put(key, src); ++ return src; ++ } ++ } ++ ++ private static void close(Source src) throws IOException { ++ synchronized (files) { ++ if (--src.refs == 0) { ++ files.remove(src.key); ++ src.close(); ++ } ++ } ++ } ++ ++ private Source(Key key, boolean toDelete) throws IOException { ++ this.key = key; ++ this.zfile = new RandomAccessFile(key.file, "r"); ++ if (toDelete) { ++ key.file.delete(); ++ } ++ try { ++ initCEN(-1); ++ byte[] buf = new byte[4]; ++ readFullyAt(buf, 0, 4, 0); ++ this.startsWithLoc = (LOCSIG(buf) == LOCSIG); ++ } catch (IOException x) { ++ try { ++ this.zfile.close(); ++ } catch (IOException xx) {} ++ throw x; ++ } ++ } ++ ++ private void close() throws IOException { ++ zfile.close(); ++ zfile = null; ++ cen = null; ++ entries = null; ++ table = null; ++ manifestNum = 0; ++ metanames = null; ++ } ++ ++ private static final int BUF_SIZE = 8192; ++ private final int readFullyAt(byte[] buf, int off, int len, long pos) ++ throws IOException ++ { ++ synchronized(zfile) { ++ zfile.seek(pos); ++ int N = len; ++ while (N > 0) { ++ int n = Math.min(BUF_SIZE, N); ++ zfile.readFully(buf, off, n); ++ off += n; ++ N -= n; ++ } ++ return len; ++ } ++ } ++ ++ private final int readAt(byte[] buf, int off, int len, long pos) ++ throws IOException ++ { ++ synchronized(zfile) { ++ zfile.seek(pos); ++ return zfile.read(buf, off, len); ++ } ++ } ++ ++ private static final int hashN(byte[] a, int off, int len) { ++ int h = 1; ++ while (len-- > 0) { ++ h = 31 * h + a[off++]; ++ } ++ return h; ++ } ++ ++ private static final int hash_append(int hash, byte b) { ++ return hash * 31 + b; ++ } + +- private static native long open(String name, int mode, long lastModified, +- boolean usemmap) throws IOException; +- private static native int getTotal(long jzfile); +- private static native boolean startsWithLOC(long jzfile); +- private static native int getManifestNum(long jzfile); +- private static native int read(long jzfile, long jzentry, +- long pos, byte[] b, int off, int len); +- +- // access to the native zentry object +- private static native long getEntryTime(long jzentry); +- private static native long getEntryCrc(long jzentry); +- private static native long getEntryCSize(long jzentry); +- private static native long getEntrySize(long jzentry); +- private static native int getEntryMethod(long jzentry); +- private static native int getEntryFlag(long jzentry); +- private static native byte[] getCommentBytes(long jzfile); +- +- private static final int JZENTRY_NAME = 0; +- private static final int JZENTRY_EXTRA = 1; +- private static final int JZENTRY_COMMENT = 2; +- private static native byte[] getEntryBytes(long jzentry, int type); +- +- private static native String getZipMessage(long jzfile); ++ private static class End { ++ int centot; // 4 bytes ++ long cenlen; // 4 bytes ++ long cenoff; // 4 bytes ++ long endpos; // 4 bytes ++ } ++ ++ /* ++ * Searches for end of central directory (END) header. The contents of ++ * the END header will be read and placed in endbuf. Returns the file ++ * position of the END header, otherwise returns -1 if the END header ++ * was not found or an error occurred. ++ */ ++ private End findEND() throws IOException { ++ long ziplen = zfile.length(); ++ if (ziplen <= 0) ++ zerror("zip file is empty"); ++ End end = new End(); ++ byte[] buf = new byte[READBLOCKSZ]; ++ long minHDR = (ziplen - END_MAXLEN) > 0 ? ziplen - END_MAXLEN : 0; ++ long minPos = minHDR - (buf.length - ENDHDR); ++ for (long pos = ziplen - buf.length; pos >= minPos; pos -= (buf.length - ENDHDR)) { ++ int off = 0; ++ if (pos < 0) { ++ // Pretend there are some NUL bytes before start of file ++ off = (int)-pos; ++ Arrays.fill(buf, 0, off, (byte)0); ++ } ++ int len = buf.length - off; ++ if (readFullyAt(buf, off, len, pos + off) != len ) { ++ zerror("zip END header not found"); ++ } ++ // Now scan the block backwards for END header signature ++ for (int i = buf.length - ENDHDR; i >= 0; i--) { ++ if (buf[i+0] == (byte)'P' && ++ buf[i+1] == (byte)'K' && ++ buf[i+2] == (byte)'\005' && ++ buf[i+3] == (byte)'\006') { ++ // Found ENDSIG header ++ byte[] endbuf = Arrays.copyOfRange(buf, i, i + ENDHDR); ++ end.centot = ENDTOT(endbuf); ++ end.cenlen = ENDSIZ(endbuf); ++ end.cenoff = ENDOFF(endbuf); ++ end.endpos = pos + i; ++ int comlen = ENDCOM(endbuf); ++ if (end.endpos + ENDHDR + comlen != ziplen) { ++ // ENDSIG matched, however the size of file comment in it does ++ // not match the real size. One "common" cause for this problem ++ // is some "extra" bytes are padded at the end of the zipfile. ++ // Let's do some extra verification, we don't care about the ++ // performance in this situation. ++ byte[] sbuf = new byte[4]; ++ long cenpos = end.endpos - end.cenlen; ++ long locpos = cenpos - end.cenoff; ++ if (cenpos < 0 || ++ locpos < 0 || ++ readFullyAt(sbuf, 0, sbuf.length, cenpos) != 4 || ++ GETSIG(sbuf) != CENSIG || ++ readFullyAt(sbuf, 0, sbuf.length, locpos) != 4 || ++ GETSIG(sbuf) != LOCSIG) { ++ continue; ++ } ++ } ++ if (comlen > 0) { // this zip file has comlen ++ comment = new byte[comlen]; ++ if (readFullyAt(comment, 0, comlen, end.endpos + ENDHDR) != comlen) { ++ zerror("zip comment read failed"); ++ } ++ } ++ if (end.cenlen == ZIP64_MAGICVAL || ++ end.cenoff == ZIP64_MAGICVAL || ++ end.centot == ZIP64_MAGICCOUNT) ++ { ++ // need to find the zip64 end; ++ try { ++ byte[] loc64 = new byte[ZIP64_LOCHDR]; ++ if (readFullyAt(loc64, 0, loc64.length, end.endpos - ZIP64_LOCHDR) ++ != loc64.length || GETSIG(loc64) != ZIP64_LOCSIG) { ++ return end; ++ } ++ long end64pos = ZIP64_LOCOFF(loc64); ++ byte[] end64buf = new byte[ZIP64_ENDHDR]; ++ if (readFullyAt(end64buf, 0, end64buf.length, end64pos) ++ != end64buf.length || GETSIG(end64buf) != ZIP64_ENDSIG) { ++ return end; ++ } ++ // end64 found, re-calcualte everything. ++ end.cenlen = ZIP64_ENDSIZ(end64buf); ++ end.cenoff = ZIP64_ENDOFF(end64buf); ++ end.centot = (int)ZIP64_ENDTOT(end64buf); // assume total < 2g ++ end.endpos = end64pos; ++ } catch (IOException x) {} // no zip64 loc/end ++ } ++ return end; ++ } ++ } ++ } ++ zerror("zip END header not found"); ++ return null; //make compiler happy ++ } ++ ++ // Reads zip file central directory. ++ private void initCEN(int knownTotal) throws IOException { ++ if (knownTotal == -1) { ++ End end = findEND(); ++ if (end.endpos == 0) { ++ locpos = 0; ++ total = 0; ++ entries = new int[0]; ++ cen = null; ++ return; // only END header present ++ } ++ if (end.cenlen > end.endpos) ++ zerror("invalid END header (bad central directory size)"); ++ long cenpos = end.endpos - end.cenlen; // position of CEN table ++ // Get position of first local file (LOC) header, taking into ++ // account that there may be a stub prefixed to the zip file. ++ locpos = cenpos - end.cenoff; ++ if (locpos < 0) { ++ zerror("invalid END header (bad central directory offset)"); ++ } ++ // read in the CEN and END ++ cen = new byte[(int)(end.cenlen + ENDHDR)]; ++ if (readFullyAt(cen, 0, cen.length, cenpos) != end.cenlen + ENDHDR) { ++ zerror("read CEN tables failed"); ++ } ++ total = end.centot; ++ } else { ++ total = knownTotal; ++ } ++ // hash table for entries ++ entries = new int[total * 3]; ++ tablelen = ((total/2) | 1); // Odd -> fewer collisions ++ table = new int[tablelen]; ++ Arrays.fill(table, ZIP_ENDCHAIN); ++ int idx = 0; ++ int hash = 0; ++ int next = -1; ++ ++ // list for all meta entries ++ ArrayList metanamesList = null; ++ ++ // Iterate through the entries in the central directory ++ int i = 0; ++ int hsh = 0; ++ int pos = 0; ++ int limit = cen.length - ENDHDR; ++ manifestNum = 0; ++ while (pos + CENHDR <= limit) { ++ if (i >= total) { ++ // This will only happen if the zip file has an incorrect ++ // ENDTOT field, which usually means it contains more than ++ // 65535 entries. ++ initCEN(countCENHeaders(cen, limit)); ++ return; ++ } ++ if (CENSIG(cen, pos) != CENSIG) ++ zerror("invalid CEN header (bad signature)"); ++ int method = CENHOW(cen, pos); ++ int nlen = CENNAM(cen, pos); ++ int elen = CENEXT(cen, pos); ++ int clen = CENCOM(cen, pos); ++ if ((CENFLG(cen, pos) & 1) != 0) ++ zerror("invalid CEN header (encrypted entry)"); ++ if (method != STORED && method != DEFLATED) ++ zerror("invalid CEN header (bad compression method: " + method + ")"); ++ if (pos + CENHDR + nlen > limit) ++ zerror("invalid CEN header (bad header size)"); ++ // Record the CEN offset and the name hash in our hash cell. ++ hash = hashN(cen, pos + CENHDR, nlen); ++ hsh = (hash & 0x7fffffff) % tablelen; ++ next = table[hsh]; ++ table[hsh] = idx; ++ idx = addEntry(idx, hash, next, pos); ++ // Adds name to metanames. ++ if (isMetaName(cen, pos + CENHDR, nlen)) { ++ if (metanamesList == null) ++ metanamesList = new ArrayList<>(4); ++ metanamesList.add(pos); ++ if (isManifestName(cen, pos + CENHDR + ++ META_INF_LEN, nlen - META_INF_LEN)) { ++ manifestNum++; ++ } ++ } ++ // skip ext and comment ++ pos += (CENHDR + nlen + elen + clen); ++ i++; ++ } ++ total = i; ++ if (metanamesList != null) { ++ metanames = new int[metanamesList.size()]; ++ for (int j = 0, len = metanames.length; j < len; j++) { ++ metanames[j] = metanamesList.get(j); ++ } ++ } ++ if (pos + ENDHDR != cen.length) { ++ zerror("invalid CEN header (bad header size)"); ++ } ++ } ++ ++ private static void zerror(String msg) throws ZipException { ++ throw new ZipException(msg); ++ } ++ ++ /* ++ * Returns the {@code pos} of the zip cen entry corresponding to the ++ * specified entry name, or -1 if not found. ++ */ ++ private int getEntryPos(byte[] name, boolean addSlash) { ++ if (total == 0) { ++ return -1; ++ } ++ int hsh = hashN(name, 0, name.length); ++ int idx = table[(hsh & 0x7fffffff) % tablelen]; ++ /* ++ * This while loop is an optimization where a double lookup ++ * for name and name+/ is being performed. The name char ++ * array has enough room at the end to try again with a ++ * slash appended if the first table lookup does not succeed. ++ */ ++ while(true) { ++ /* ++ * Search down the target hash chain for a entry whose ++ * 32 bit hash matches the hashed name. ++ */ ++ while (idx != ZIP_ENDCHAIN) { ++ if (getEntryHash(idx) == hsh) { ++ // The CEN name must match the specfied one ++ int pos = getEntryPos(idx); ++ if (name.length == CENNAM(cen, pos)) { ++ boolean matched = true; ++ int nameoff = pos + CENHDR; ++ for (int i = 0; i < name.length; i++) { ++ if (name[i] != cen[nameoff++]) { ++ matched = false; ++ break; ++ } ++ } ++ if (matched) { ++ return pos; ++ } ++ } ++ } ++ idx = getEntryNext(idx); ++ } ++ /* If not addSlash, or slash is already there, we are done */ ++ if (!addSlash || name[name.length - 1] == '/') { ++ return -1; ++ } ++ /* Add slash and try once more */ ++ name = Arrays.copyOf(name, name.length + 1); ++ name[name.length - 1] = '/'; ++ hsh = hash_append(hsh, (byte)'/'); ++ //idx = table[hsh % tablelen]; ++ idx = table[(hsh & 0x7fffffff) % tablelen]; ++ addSlash = false; ++ } ++ } ++ ++ /** ++ * Returns true if the bytes represent a non-directory name ++ * beginning with "META-INF/", disregarding ASCII case. ++ */ ++ private static boolean isMetaName(byte[] name, int off, int len) { ++ // Use the "oldest ASCII trick in the book" ++ return len > 9 // "META-INF/".length() ++ && name[off + len - 1] != '/' // non-directory ++ && (name[off++] | 0x20) == 'm' ++ && (name[off++] | 0x20) == 'e' ++ && (name[off++] | 0x20) == 't' ++ && (name[off++] | 0x20) == 'a' ++ && (name[off++] ) == '-' ++ && (name[off++] | 0x20) == 'i' ++ && (name[off++] | 0x20) == 'n' ++ && (name[off++] | 0x20) == 'f' ++ && (name[off] ) == '/'; ++ } ++ ++ /* ++ * Check if the bytes represents a name equals to MANIFEST.MF ++ */ ++ private boolean isManifestName(byte[] name, int off, int len) { ++ return (len == 11 // "MANIFEST.MF".length() ++ && (name[off++] | 0x20) == 'm' ++ && (name[off++] | 0x20) == 'a' ++ && (name[off++] | 0x20) == 'n' ++ && (name[off++] | 0x20) == 'i' ++ && (name[off++] | 0x20) == 'f' ++ && (name[off++] | 0x20) == 'e' ++ && (name[off++] | 0x20) == 's' ++ && (name[off++] | 0x20) == 't' ++ && (name[off++] ) == '.' ++ && (name[off++] | 0x20) == 'm' ++ && (name[off] | 0x20) == 'f'); ++ } ++ ++ /* ++ * Counts the number of CEN headers in a central directory extending ++ * from BEG to END. Might return a bogus answer if the zip file is ++ * corrupt, but will not crash. ++ */ ++ static int countCENHeaders(byte[] cen, int end) { ++ int count = 0; ++ int pos = 0; ++ while (pos + CENHDR <= end) { ++ count++; ++ pos += (CENHDR + CENNAM(cen, pos) + CENEXT(cen, pos) + CENCOM(cen, pos)); ++ } ++ return count; ++ } ++ } + } +diff --git a/jdk/src/share/classes/java/util/zip/ZipUtils.java b/jdk/src/share/classes/java/util/zip/ZipUtils.java +index cd8b05278..a9b9db948 100644 +--- a/jdk/src/share/classes/java/util/zip/ZipUtils.java ++++ b/jdk/src/share/classes/java/util/zip/ZipUtils.java +@@ -29,6 +29,8 @@ import java.nio.file.attribute.FileTime; + import java.util.Date; + import java.util.concurrent.TimeUnit; + ++import static java.util.zip.ZipConstants.ENDHDR; ++ + class ZipUtils { + + // used to adjust values between Windows and java epoch +@@ -126,7 +128,7 @@ class ZipUtils { + * The bytes are assumed to be in Intel (little-endian) byte order. + */ + public static final int get16(byte b[], int off) { +- return Byte.toUnsignedInt(b[off]) | (Byte.toUnsignedInt(b[off+1]) << 8); ++ return (b[off] & 0xff) | ((b[off + 1] & 0xff) << 8); + } + + /** +@@ -144,4 +146,79 @@ class ZipUtils { + public static final long get64(byte b[], int off) { + return get32(b, off) | (get32(b, off+4) << 32); + } ++ ++ // fields access methods ++ static final int CH(byte[] b, int n) { ++ return b[n] & 0xff ; ++ } ++ ++ static final int SH(byte[] b, int n) { ++ return (b[n] & 0xff) | ((b[n + 1] & 0xff) << 8); ++ } ++ ++ static final long LG(byte[] b, int n) { ++ return ((SH(b, n)) | (SH(b, n + 2) << 16)) & 0xffffffffL; ++ } ++ ++ static final long LL(byte[] b, int n) { ++ return (LG(b, n)) | (LG(b, n + 4) << 32); ++ } ++ ++ static final long GETSIG(byte[] b) { ++ return LG(b, 0); ++ } ++ ++ // local file (LOC) header fields ++ static final long LOCSIG(byte[] b) { return LG(b, 0); } // signature ++ static final int LOCVER(byte[] b) { return SH(b, 4); } // version needed to extract ++ static final int LOCFLG(byte[] b) { return SH(b, 6); } // general purpose bit flags ++ static final int LOCHOW(byte[] b) { return SH(b, 8); } // compression method ++ static final long LOCTIM(byte[] b) { return LG(b, 10);} // modification time ++ static final long LOCCRC(byte[] b) { return LG(b, 14);} // crc of uncompressed data ++ static final long LOCSIZ(byte[] b) { return LG(b, 18);} // compressed data size ++ static final long LOCLEN(byte[] b) { return LG(b, 22);} // uncompressed data size ++ static final int LOCNAM(byte[] b) { return SH(b, 26);} // filename length ++ static final int LOCEXT(byte[] b) { return SH(b, 28);} // extra field length ++ ++ // extra local (EXT) header fields ++ static final long EXTCRC(byte[] b) { return LG(b, 4);} // crc of uncompressed data ++ static final long EXTSIZ(byte[] b) { return LG(b, 8);} // compressed size ++ static final long EXTLEN(byte[] b) { return LG(b, 12);} // uncompressed size ++ ++ // end of central directory header (END) fields ++ static final int ENDSUB(byte[] b) { return SH(b, 8); } // number of entries on this disk ++ static final int ENDTOT(byte[] b) { return SH(b, 10);} // total number of entries ++ static final long ENDSIZ(byte[] b) { return LG(b, 12);} // central directory size ++ static final long ENDOFF(byte[] b) { return LG(b, 16);} // central directory offset ++ static final int ENDCOM(byte[] b) { return SH(b, 20);} // size of zip file comment ++ static final int ENDCOM(byte[] b, int off) { return SH(b, off + 20);} ++ ++ // zip64 end of central directory recoder fields ++ static final long ZIP64_ENDTOD(byte[] b) { return LL(b, 24);} // total number of entries on disk ++ static final long ZIP64_ENDTOT(byte[] b) { return LL(b, 32);} // total number of entries ++ static final long ZIP64_ENDSIZ(byte[] b) { return LL(b, 40);} // central directory size ++ static final long ZIP64_ENDOFF(byte[] b) { return LL(b, 48);} // central directory offset ++ static final long ZIP64_LOCOFF(byte[] b) { return LL(b, 8);} // zip64 end offset ++ ++ // central directory header (CEN) fields ++ static final long CENSIG(byte[] b, int pos) { return LG(b, pos + 0); } ++ static final int CENVEM(byte[] b, int pos) { return SH(b, pos + 4); } ++ static final int CENVER(byte[] b, int pos) { return SH(b, pos + 6); } ++ static final int CENFLG(byte[] b, int pos) { return SH(b, pos + 8); } ++ static final int CENHOW(byte[] b, int pos) { return SH(b, pos + 10);} ++ static final long CENTIM(byte[] b, int pos) { return LG(b, pos + 12);} ++ static final long CENCRC(byte[] b, int pos) { return LG(b, pos + 16);} ++ static final long CENSIZ(byte[] b, int pos) { return LG(b, pos + 20);} ++ static final long CENLEN(byte[] b, int pos) { return LG(b, pos + 24);} ++ static final int CENNAM(byte[] b, int pos) { return SH(b, pos + 28);} ++ static final int CENEXT(byte[] b, int pos) { return SH(b, pos + 30);} ++ static final int CENCOM(byte[] b, int pos) { return SH(b, pos + 32);} ++ static final int CENDSK(byte[] b, int pos) { return SH(b, pos + 34);} ++ static final int CENATT(byte[] b, int pos) { return SH(b, pos + 36);} ++ static final long CENATX(byte[] b, int pos) { return LG(b, pos + 38);} ++ static final long CENOFF(byte[] b, int pos) { return LG(b, pos + 42);} ++ ++ // The END header is followed by a variable length comment of size < 64k. ++ static final long END_MAXLEN = 0xFFFF + ENDHDR; ++ static final int READBLOCKSZ = 128; + } +diff --git a/jdk/src/share/classes/sun/misc/JavaUtilZipFileAccess.java b/jdk/src/share/classes/sun/misc/JavaUtilZipFileAccess.java +index 0d931d1db..d140f54a5 100644 +--- a/jdk/src/share/classes/sun/misc/JavaUtilZipFileAccess.java ++++ b/jdk/src/share/classes/sun/misc/JavaUtilZipFileAccess.java +@@ -31,5 +31,6 @@ import java.util.zip.ZipFile; + public interface JavaUtilZipFileAccess { + public boolean startsWithLocHeader(ZipFile zip); + public int getManifestNum(JarFile zip); ++ public String[] getMetaInfEntryNames(ZipFile zip); + } + +diff --git a/jdk/src/share/classes/sun/misc/VM.java b/jdk/src/share/classes/sun/misc/VM.java +index 3e64628c6..cb757b3e3 100644 +--- a/jdk/src/share/classes/sun/misc/VM.java ++++ b/jdk/src/share/classes/sun/misc/VM.java +@@ -310,9 +310,6 @@ public class VM { + // used by java.lang.Integer.IntegerCache + props.remove("java.lang.Integer.IntegerCache.high"); + +- // used by java.util.zip.ZipFile +- props.remove("sun.zip.disableMemoryMapping"); +- + // used by sun.launcher.LauncherHelper + props.remove("sun.java.launcher.diag"); + +diff --git a/jdk/src/share/native/java/util/zip/ZipFile.c b/jdk/src/share/native/java/util/zip/ZipFile.c +deleted file mode 100644 +index 8e3698290..000000000 +--- a/jdk/src/share/native/java/util/zip/ZipFile.c ++++ /dev/null +@@ -1,403 +0,0 @@ +-/* +- * Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved. +- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +- * +- * This code is free software; you can redistribute it and/or modify it +- * under the terms of the GNU General Public License version 2 only, as +- * published by the Free Software Foundation. Oracle designates this +- * particular file as subject to the "Classpath" exception as provided +- * by Oracle in the LICENSE file that accompanied this code. +- * +- * This code is distributed in the hope that it will be useful, but WITHOUT +- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +- * version 2 for more details (a copy is included in the LICENSE file that +- * accompanied this code). +- * +- * You should have received a copy of the GNU General Public License version +- * 2 along with this work; if not, write to the Free Software Foundation, +- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +- * +- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +- * or visit www.oracle.com if you need additional information or have any +- * questions. +- */ +- +-/* +- * Native method support for java.util.zip.ZipFile +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include "jlong.h" +-#include "jvm.h" +-#include "jni.h" +-#include "jni_util.h" +-#include "zip_util.h" +-#ifdef WIN32 +-#include "io_util_md.h" +-#else +-#include "io_util.h" +-#endif +- +-#include "java_util_zip_ZipFile.h" +-#include "java_util_jar_JarFile.h" +- +-#define DEFLATED 8 +-#define STORED 0 +- +-static jfieldID jzfileID; +- +-static int OPEN_READ = java_util_zip_ZipFile_OPEN_READ; +-static int OPEN_DELETE = java_util_zip_ZipFile_OPEN_DELETE; +- +-JNIEXPORT void JNICALL +-Java_java_util_zip_ZipFile_initIDs(JNIEnv *env, jclass cls) +-{ +- jzfileID = (*env)->GetFieldID(env, cls, "jzfile", "J"); +- assert(jzfileID != 0); +-} +- +-static void +-ThrowZipException(JNIEnv *env, const char *msg) +-{ +- jstring s = NULL; +- jobject x; +- +- if (msg != NULL) { +- s = JNU_NewStringPlatform(env, msg); +- } +- if (s != NULL) { +- x = JNU_NewObjectByName(env, +- "java/util/zip/ZipException", +- "(Ljava/lang/String;)V", s); +- if (x != NULL) { +- (*env)->Throw(env, x); +- } +- } +-} +- +-JNIEXPORT jlong JNICALL +-Java_java_util_zip_ZipFile_open(JNIEnv *env, jclass cls, jstring name, +- jint mode, jlong lastModified, +- jboolean usemmap) +-{ +- const char *path = JNU_GetStringPlatformChars(env, name, 0); +- char *msg = 0; +- jlong result = 0; +- int flag = 0; +- jzfile *zip = 0; +- +- if (mode & OPEN_READ) flag |= O_RDONLY; +- if (mode & OPEN_DELETE) flag |= JVM_O_DELETE; +- +- if (path != 0) { +- zip = ZIP_Get_From_Cache(path, &msg, lastModified); +- if (zip == 0 && msg == 0) { +- ZFILE zfd = 0; +-#ifdef WIN32 +- zfd = winFileHandleOpen(env, name, flag); +- if (zfd == -1) { +- /* Exception already pending. */ +- goto finally; +- } +-#else +- zfd = JVM_Open(path, flag, 0); +- if (zfd < 0) { +- throwFileNotFoundException(env, name); +- goto finally; +- } +-#endif +- zip = ZIP_Put_In_Cache0(path, zfd, &msg, lastModified, usemmap); +- } +- +- if (zip != 0) { +- result = ptr_to_jlong(zip); +- } else if (msg != 0) { +- ThrowZipException(env, msg); +- free(msg); +- } else if (errno == ENOMEM) { +- JNU_ThrowOutOfMemoryError(env, 0); +- } else { +- ThrowZipException(env, "error in opening zip file"); +- } +-finally: +- JNU_ReleaseStringPlatformChars(env, name, path); +- } +- return result; +-} +- +-JNIEXPORT jint JNICALL +-Java_java_util_zip_ZipFile_getTotal(JNIEnv *env, jclass cls, jlong zfile) +-{ +- jzfile *zip = jlong_to_ptr(zfile); +- +- return zip->total; +-} +- +-JNIEXPORT jboolean JNICALL +-Java_java_util_zip_ZipFile_startsWithLOC(JNIEnv *env, jclass cls, jlong zfile) +-{ +- jzfile *zip = jlong_to_ptr(zfile); +- +- return zip->locsig; +-} +- +-JNIEXPORT jint JNICALL +-Java_java_util_zip_ZipFile_getManifestNum(JNIEnv *env, jclass cls, jlong zfile) +-{ +- jzfile *zip = jlong_to_ptr(zfile); +- +- return zip->manifestNum; +-} +- +-JNIEXPORT void JNICALL +-Java_java_util_zip_ZipFile_close(JNIEnv *env, jclass cls, jlong zfile) +-{ +- ZIP_Close(jlong_to_ptr(zfile)); +-} +- +-JNIEXPORT jlong JNICALL +-Java_java_util_zip_ZipFile_getEntry(JNIEnv *env, jclass cls, jlong zfile, +- jbyteArray name, jboolean addSlash) +-{ +-#define MAXNAME 1024 +- jzfile *zip = jlong_to_ptr(zfile); +- jsize ulen = (*env)->GetArrayLength(env, name); +- char buf[MAXNAME+2], *path; +- jzentry *ze; +- +- if (ulen > MAXNAME) { +- path = malloc(ulen + 2); +- if (path == 0) { +- JNU_ThrowOutOfMemoryError(env, 0); +- return 0; +- } +- } else { +- path = buf; +- } +- (*env)->GetByteArrayRegion(env, name, 0, ulen, (jbyte *)path); +- path[ulen] = '\0'; +- ze = ZIP_GetEntry2(zip, path, (jint)ulen, addSlash); +- if (path != buf) { +- free(path); +- } +- return ptr_to_jlong(ze); +-} +- +-JNIEXPORT void JNICALL +-Java_java_util_zip_ZipFile_freeEntry(JNIEnv *env, jclass cls, jlong zfile, +- jlong zentry) +-{ +- jzfile *zip = jlong_to_ptr(zfile); +- jzentry *ze = jlong_to_ptr(zentry); +- ZIP_FreeEntry(zip, ze); +-} +- +-JNIEXPORT jlong JNICALL +-Java_java_util_zip_ZipFile_getNextEntry(JNIEnv *env, jclass cls, jlong zfile, +- jint n) +-{ +- jzentry *ze = ZIP_GetNextEntry(jlong_to_ptr(zfile), n); +- return ptr_to_jlong(ze); +-} +- +-JNIEXPORT jint JNICALL +-Java_java_util_zip_ZipFile_getEntryMethod(JNIEnv *env, jclass cls, jlong zentry) +-{ +- jzentry *ze = jlong_to_ptr(zentry); +- return ze->csize != 0 ? DEFLATED : STORED; +-} +- +-JNIEXPORT jint JNICALL +-Java_java_util_zip_ZipFile_getEntryFlag(JNIEnv *env, jclass cls, jlong zentry) +-{ +- jzentry *ze = jlong_to_ptr(zentry); +- return ze->flag; +-} +- +-JNIEXPORT jlong JNICALL +-Java_java_util_zip_ZipFile_getEntryCSize(JNIEnv *env, jclass cls, jlong zentry) +-{ +- jzentry *ze = jlong_to_ptr(zentry); +- return ze->csize != 0 ? ze->csize : ze->size; +-} +- +-JNIEXPORT jlong JNICALL +-Java_java_util_zip_ZipFile_getEntrySize(JNIEnv *env, jclass cls, jlong zentry) +-{ +- jzentry *ze = jlong_to_ptr(zentry); +- return ze->size; +-} +- +-JNIEXPORT jlong JNICALL +-Java_java_util_zip_ZipFile_getEntryTime(JNIEnv *env, jclass cls, jlong zentry) +-{ +- jzentry *ze = jlong_to_ptr(zentry); +- return (jlong)ze->time & 0xffffffffUL; +-} +- +-JNIEXPORT jlong JNICALL +-Java_java_util_zip_ZipFile_getEntryCrc(JNIEnv *env, jclass cls, jlong zentry) +-{ +- jzentry *ze = jlong_to_ptr(zentry); +- return (jlong)ze->crc & 0xffffffffUL; +-} +- +-JNIEXPORT jbyteArray JNICALL +-Java_java_util_zip_ZipFile_getCommentBytes(JNIEnv *env, +- jclass cls, +- jlong zfile) +-{ +- jzfile *zip = jlong_to_ptr(zfile); +- jbyteArray jba = NULL; +- +- if (zip->comment != NULL) { +- if ((jba = (*env)->NewByteArray(env, zip->clen)) == NULL) +- return NULL; +- (*env)->SetByteArrayRegion(env, jba, 0, zip->clen, (jbyte*)zip->comment); +- } +- return jba; +-} +- +-JNIEXPORT jbyteArray JNICALL +-Java_java_util_zip_ZipFile_getEntryBytes(JNIEnv *env, +- jclass cls, +- jlong zentry, jint type) +-{ +- jzentry *ze = jlong_to_ptr(zentry); +- int len = 0; +- jbyteArray jba = NULL; +- switch (type) { +- case java_util_zip_ZipFile_JZENTRY_NAME: +- if (ze->name != 0) { +- len = (int)ze->nlen; +- if (len == 0 || (jba = (*env)->NewByteArray(env, len)) == NULL) +- break; +- (*env)->SetByteArrayRegion(env, jba, 0, len, (jbyte *)ze->name); +- } +- break; +- case java_util_zip_ZipFile_JZENTRY_EXTRA: +- if (ze->extra != 0) { +- unsigned char *bp = (unsigned char *)&ze->extra[0]; +- len = (bp[0] | (bp[1] << 8)); +- if (len <= 0 || (jba = (*env)->NewByteArray(env, len)) == NULL) +- break; +- (*env)->SetByteArrayRegion(env, jba, 0, len, &ze->extra[2]); +- } +- break; +- case java_util_zip_ZipFile_JZENTRY_COMMENT: +- if (ze->comment != 0) { +- len = (int)strlen(ze->comment); +- if (len == 0 || (jba = (*env)->NewByteArray(env, len)) == NULL) +- break; +- (*env)->SetByteArrayRegion(env, jba, 0, len, (jbyte*)ze->comment); +- } +- break; +- } +- return jba; +-} +- +-JNIEXPORT jint JNICALL +-Java_java_util_zip_ZipFile_read(JNIEnv *env, jclass cls, jlong zfile, +- jlong zentry, jlong pos, jbyteArray bytes, +- jint off, jint len) +-{ +- jzfile *zip = jlong_to_ptr(zfile); +- char *msg; +- +-#define BUFSIZE 8192 +- /* copy via tmp stack buffer: */ +- jbyte buf[BUFSIZE]; +- +- if (len > BUFSIZE) { +- len = BUFSIZE; +- } +- +- ZIP_Lock(zip); +- len = ZIP_Read(zip, jlong_to_ptr(zentry), pos, buf, len); +- msg = zip->msg; +- ZIP_Unlock(zip); +- if (len != -1) { +- (*env)->SetByteArrayRegion(env, bytes, off, len, buf); +- } +- +- if (len == -1) { +- if (msg != 0) { +- ThrowZipException(env, msg); +- } else { +- char errmsg[128]; +- sprintf(errmsg, "errno: %d, error: %s\n", +- errno, "Error reading ZIP file"); +- JNU_ThrowIOExceptionWithLastError(env, errmsg); +- } +- } +- +- return len; +-} +- +-/* +- * Returns an array of strings representing the names of all entries +- * that begin with "META-INF/" (case ignored). This native method is +- * used in JarFile as an optimization when looking up manifest and +- * signature file entries. Returns null if no entries were found. +- */ +-JNIEXPORT jobjectArray JNICALL +-Java_java_util_jar_JarFile_getMetaInfEntryNames(JNIEnv *env, jobject obj) +-{ +- jlong zfile = (*env)->GetLongField(env, obj, jzfileID); +- jzfile *zip; +- int i, count; +- jobjectArray result = 0; +- +- if (zfile == 0) { +- JNU_ThrowByName(env, +- "java/lang/IllegalStateException", "zip file closed"); +- return NULL; +- } +- zip = jlong_to_ptr(zfile); +- +- /* count the number of valid ZIP metanames */ +- count = 0; +- if (zip->metanames != 0) { +- for (i = 0; i < zip->metacount; i++) { +- if (zip->metanames[i] != 0) { +- count++; +- } +- } +- } +- +- /* If some names were found then build array of java strings */ +- if (count > 0) { +- jclass cls = JNU_ClassString(env); +- CHECK_NULL_RETURN(cls, NULL); +- result = (*env)->NewObjectArray(env, count, cls, 0); +- CHECK_NULL_RETURN(result, NULL); +- if (result != 0) { +- for (i = 0; i < count; i++) { +- jstring str = (*env)->NewStringUTF(env, zip->metanames[i]); +- if (str == 0) { +- break; +- } +- (*env)->SetObjectArrayElement(env, result, i, str); +- (*env)->DeleteLocalRef(env, str); +- } +- } +- } +- return result; +-} +- +-JNIEXPORT jstring JNICALL +-Java_java_util_zip_ZipFile_getZipMessage(JNIEnv *env, jclass cls, jlong zfile) +-{ +- jzfile *zip = jlong_to_ptr(zfile); +- char *msg = zip->msg; +- if (msg == NULL) { +- return NULL; +- } +- return JNU_NewStringPlatform(env, msg); +-} +diff --git a/jdk/src/share/native/java/util/zip/zip_util.c b/jdk/src/share/native/java/util/zip/zip_util.c +index ff59c5ecc..ffe094065 100644 +--- a/jdk/src/share/native/java/util/zip/zip_util.c ++++ b/jdk/src/share/native/java/util/zip/zip_util.c +@@ -74,8 +74,6 @@ static void freeCEN(jzfile *); + #define PATH_MAX 1024 + #endif + +-#define META_INF_LEN 9 /* "META-INF/".length() */ +- + static jint INITIAL_META_COUNT = 2; /* initial number of entries in meta name array */ + + #ifdef LINUX +@@ -475,25 +473,6 @@ isMetaName(const char *name, int length) + return 1; + } + +-/* +- * Check if the bytes represents a name equals to MANIFEST.MF +- */ +-static int +-isManifestName(const char *name, int length) +-{ +- const char *s; +- if (length != (int)sizeof("MANIFEST.MF") - 1) +- return 0; +- for (s = "MANIFEST.MF"; *s != '\0'; s++) { +- char c = *name++; +- // Avoid toupper; it's locale-dependent +- if (c >= 'a' && c <= 'z') c += 'A' - 'a'; +- if (*s != c) +- return 0; +- } +- return 1; +-} +- + /* + * Increases the capacity of zip->metanames. + * Returns non-zero in case of allocation error. +@@ -564,7 +543,6 @@ freeCEN(jzfile *zip) + { + free(zip->entries); zip->entries = NULL; + free(zip->table); zip->table = NULL; +- zip->manifestNum = 0; + freeMetaNames(zip); + } + +@@ -718,8 +696,6 @@ readCEN(jzfile *zip, jint knownTotal) + for (j = 0; j < tablelen; j++) + table[j] = ZIP_ENDCHAIN; + +- zip->manifestNum = 0; +- + /* Iterate through the entries in the central directory */ + for (i = 0, cp = cenbuf; cp <= cenend - CENHDR; i++, cp += CENSIZE(cp)) { + /* Following are unsigned 16-bit */ +@@ -747,12 +723,9 @@ readCEN(jzfile *zip, jint knownTotal) + ZIP_FORMAT_ERROR("invalid CEN header (bad header size)"); + + /* if the entry is metadata add it to our metadata names */ +- if (isMetaName((char *)cp+CENHDR, nlen)) { +- if (isManifestName((char *)cp+CENHDR+META_INF_LEN, nlen-META_INF_LEN)) +- zip->manifestNum++; ++ if (isMetaName((char *)cp+CENHDR, nlen)) + if (addMetaName(zip, (char *)cp+CENHDR, nlen) != 0) + goto Catch; +- } + + /* Record the CEN offset and the name hash in our hash cell. */ + entries[i].cenpos = cenpos + (cp - cenbuf); +diff --git a/jdk/src/share/native/java/util/zip/zip_util.h b/jdk/src/share/native/java/util/zip/zip_util.h +index 2a7ae4b7e..a64668cd6 100644 +--- a/jdk/src/share/native/java/util/zip/zip_util.h ++++ b/jdk/src/share/native/java/util/zip/zip_util.h +@@ -229,7 +229,6 @@ typedef struct jzfile { /* Zip file */ + char **metanames; /* array of meta names (may have null names) */ + jint metacurrent; /* the next empty slot in metanames array */ + jint metacount; /* number of slots in metanames array */ +- jint manifestNum; /* number of META-INF/MANIFEST.MF, case insensitive */ + jlong lastModified; /* last modified time */ + jlong locpos; /* position of first LOC header (usually 0) */ + } jzfile; +diff --git a/jdk/test/java/nio/file/spi/TestProvider.java b/jdk/test/java/nio/file/spi/TestProvider.java +index b2744f4c0..c975b8e27 100644 +--- a/jdk/test/java/nio/file/spi/TestProvider.java ++++ b/jdk/test/java/nio/file/spi/TestProvider.java +@@ -21,20 +21,34 @@ + * questions. + */ + +-import java.nio.file.spi.FileSystemProvider; ++import java.io.File; + import java.nio.file.*; +-import java.nio.file.attribute.*; ++import java.nio.file.attribute.BasicFileAttributes; ++import java.nio.file.attribute.FileAttribute; ++import java.nio.file.attribute.FileAttributeView; ++import java.nio.file.attribute.UserPrincipalLookupService; ++import java.nio.file.spi.FileSystemProvider; + import java.nio.channels.SeekableByteChannel; + import java.net.URI; +-import java.util.*; + import java.io.IOException; ++import java.util.Collections; ++import java.util.Iterator; ++import java.util.Map; ++import java.util.Set; + + public class TestProvider extends FileSystemProvider { + +- private final FileSystem theFileSystem; ++ private final FileSystemProvider defaultProvider; ++ private final TestFileSystem theFileSystem; + + public TestProvider(FileSystemProvider defaultProvider) { +- theFileSystem = new TestFileSystem(this); ++ this.defaultProvider = defaultProvider; ++ FileSystem fs = defaultProvider.getFileSystem(URI.create("file:/")); ++ this.theFileSystem = new TestFileSystem(fs, this); ++ } ++ ++ FileSystemProvider defaultProvider() { ++ return defaultProvider; + } + + @Override +@@ -43,8 +57,8 @@ public class TestProvider extends FileSystemProvider { + } + + @Override +- public FileSystem newFileSystem(URI uri, Map env) { +- throw new RuntimeException("not implemented"); ++ public FileSystem newFileSystem(URI uri, Map env) throws IOException { ++ return defaultProvider.newFileSystem(uri, env); + } + + @Override +@@ -54,7 +68,8 @@ public class TestProvider extends FileSystemProvider { + + @Override + public Path getPath(URI uri) { +- throw new RuntimeException("not implemented"); ++ Path path = defaultProvider.getPath(uri); ++ return theFileSystem.wrap(path); + } + + @Override +@@ -70,7 +85,8 @@ public class TestProvider extends FileSystemProvider { + LinkOption... options) + throws IOException + { +- throw new RuntimeException("not implemented"); ++ Path delegate = theFileSystem.unwrap(file); ++ return defaultProvider.readAttributes(delegate, attributes, options); + } + + @Override +@@ -79,7 +95,8 @@ public class TestProvider extends FileSystemProvider { + LinkOption... options) + throws IOException + { +- throw new RuntimeException("not implemented"); ++ Path delegate = theFileSystem.unwrap(file); ++ return defaultProvider.readAttributes(delegate, type, options); + } + + @Override +@@ -87,13 +104,14 @@ public class TestProvider extends FileSystemProvider { + Class type, + LinkOption... options) + { +- throw new RuntimeException("not implemented"); ++ Path delegate = theFileSystem.unwrap(file); ++ return defaultProvider.getFileAttributeView(delegate, type, options); + } + +- + @Override + public void delete(Path file) throws IOException { +- throw new RuntimeException("not implemented"); ++ Path delegate = theFileSystem.unwrap(file); ++ defaultProvider.delete(delegate); + } + + @Override +@@ -110,10 +128,11 @@ public class TestProvider extends FileSystemProvider { + + @Override + public Path readSymbolicLink(Path link) throws IOException { +- throw new RuntimeException("not implemented"); ++ Path delegate = theFileSystem.unwrap(link); ++ Path target = defaultProvider.readSymbolicLink(delegate); ++ return theFileSystem.wrap(target); + } + +- + @Override + public void copy(Path source, Path target, CopyOption... options) + throws IOException +@@ -140,7 +159,8 @@ public class TestProvider extends FileSystemProvider { + public void createDirectory(Path dir, FileAttribute... attrs) + throws IOException + { +- throw new RuntimeException("not implemented"); ++ Path delegate = theFileSystem.unwrap(dir); ++ defaultProvider.createDirectory(delegate, attrs); + } + + @Override +@@ -149,13 +169,13 @@ public class TestProvider extends FileSystemProvider { + FileAttribute... attrs) + throws IOException + { +- throw new RuntimeException("not implemented"); ++ Path delegate = theFileSystem.unwrap(file); ++ return defaultProvider.newByteChannel(delegate, options, attrs); + } + +- + @Override + public boolean isHidden(Path file) throws IOException { +- throw new RuntimeException("not implemented"); ++ throw new ReadOnlyFileSystemException(); + } + + @Override +@@ -176,12 +196,26 @@ public class TestProvider extends FileSystemProvider { + } + + static class TestFileSystem extends FileSystem { ++ private final FileSystem delegate; + private final TestProvider provider; + +- TestFileSystem(TestProvider provider) { ++ TestFileSystem(FileSystem delegate, TestProvider provider) { ++ this.delegate = delegate; + this.provider = provider; + } + ++ Path wrap(Path path) { ++ return (path != null) ? new TestPath(this, path) : null; ++ } ++ ++ Path unwrap(Path wrapper) { ++ if (wrapper == null) ++ throw new NullPointerException(); ++ if (!(wrapper instanceof TestPath)) ++ throw new ProviderMismatchException(); ++ return ((TestPath)wrapper).unwrap(); ++ } ++ + @Override + public FileSystemProvider provider() { + return provider; +@@ -194,17 +228,17 @@ public class TestProvider extends FileSystemProvider { + + @Override + public boolean isOpen() { +- throw new RuntimeException("not implemented"); ++ return true; + } + + @Override + public boolean isReadOnly() { +- throw new RuntimeException("not implemented"); ++ return false; + } + + @Override + public String getSeparator() { +- throw new RuntimeException("not implemented"); ++ return delegate.getSeparator(); + } + + @Override +@@ -219,27 +253,209 @@ public class TestProvider extends FileSystemProvider { + + @Override + public Set supportedFileAttributeViews() { +- throw new RuntimeException("not implemented"); ++ return delegate.supportedFileAttributeViews(); + } + + @Override + public Path getPath(String first, String... more) { +- throw new RuntimeException("not implemented"); ++ Path path = delegate.getPath(first, more); ++ return wrap(path); + } + + @Override + public PathMatcher getPathMatcher(String syntaxAndPattern) { +- throw new RuntimeException("not implemented"); ++ return delegate.getPathMatcher(syntaxAndPattern); + } + + @Override + public UserPrincipalLookupService getUserPrincipalLookupService() { +- throw new RuntimeException("not implemented"); ++ return delegate.getUserPrincipalLookupService(); + } + + @Override + public WatchService newWatchService() throws IOException { +- throw new RuntimeException("not implemented"); ++ throw new UnsupportedOperationException(); ++ } ++ } ++ ++ static class TestPath implements Path { ++ private final TestFileSystem fs; ++ private final Path delegate; ++ ++ TestPath(TestFileSystem fs, Path delegate) { ++ this.fs = fs; ++ this.delegate = delegate; ++ } ++ ++ Path unwrap() { ++ return delegate; ++ } ++ ++ @Override ++ public FileSystem getFileSystem() { ++ return fs; ++ } ++ ++ @Override ++ public boolean isAbsolute() { ++ return delegate.isAbsolute(); ++ } ++ ++ @Override ++ public Path getRoot() { ++ return fs.wrap(delegate.getRoot()); ++ } ++ ++ @Override ++ public Path getParent() { ++ return fs.wrap(delegate.getParent()); ++ } ++ ++ @Override ++ public int getNameCount() { ++ return delegate.getNameCount(); ++ } ++ ++ @Override ++ public Path getFileName() { ++ return fs.wrap(delegate.getFileName()); ++ } ++ ++ @Override ++ public Path getName(int index) { ++ return fs.wrap(delegate.getName(index)); ++ } ++ ++ @Override ++ public Path subpath(int beginIndex, int endIndex) { ++ return fs.wrap(delegate.subpath(beginIndex, endIndex)); ++ } ++ ++ @Override ++ public boolean startsWith(Path other) { ++ return delegate.startsWith(fs.unwrap(other)); ++ } ++ ++ @Override ++ public boolean startsWith(String other) { ++ return delegate.startsWith(other); ++ } ++ ++ @Override ++ public boolean endsWith(Path other) { ++ return delegate.endsWith(fs.unwrap(other)); ++ } ++ ++ @Override ++ public boolean endsWith(String other) { ++ return delegate.endsWith(other); ++ } ++ ++ @Override ++ public Path normalize() { ++ return fs.wrap(delegate.normalize()); ++ } ++ ++ @Override ++ public Path resolve(Path other) { ++ return fs.wrap(delegate.resolve(fs.unwrap(other))); ++ } ++ ++ @Override ++ public Path resolve(String other) { ++ return fs.wrap(delegate.resolve(other)); ++ } ++ ++ @Override ++ public Path resolveSibling(Path other) { ++ return fs.wrap(delegate.resolveSibling(fs.unwrap(other))); ++ } ++ ++ @Override ++ public Path resolveSibling(String other) { ++ return fs.wrap(delegate.resolveSibling(other)); ++ } ++ ++ @Override ++ public Path relativize(Path other) { ++ return fs.wrap(delegate.relativize(fs.unwrap(other))); ++ } ++ ++ @Override ++ public boolean equals(Object other) { ++ if (!(other instanceof TestPath)) ++ return false; ++ return delegate.equals(fs.unwrap((TestPath) other)); ++ } ++ ++ @Override ++ public int hashCode() { ++ return delegate.hashCode(); ++ } ++ ++ @Override ++ public String toString() { ++ return delegate.toString(); ++ } ++ ++ @Override ++ public URI toUri() { ++ String ssp = delegate.toUri().getSchemeSpecificPart(); ++ return URI.create(fs.provider().getScheme() + ":" + ssp); ++ } ++ ++ @Override ++ public Path toAbsolutePath() { ++ return fs.wrap(delegate.toAbsolutePath()); ++ } ++ ++ @Override ++ public Path toRealPath(LinkOption... options) throws IOException { ++ return fs.wrap(delegate.toRealPath(options)); ++ } ++ ++ @Override ++ public File toFile() { ++ return new File(toString()); ++ } ++ ++ @Override ++ public Iterator iterator() { ++ final Iterator itr = delegate.iterator(); ++ return new Iterator() { ++ @Override ++ public boolean hasNext() { ++ return itr.hasNext(); ++ } ++ @Override ++ public Path next() { ++ return fs.wrap(itr.next()); ++ } ++ @Override ++ public void remove() { ++ itr.remove(); ++ } ++ }; ++ } ++ ++ @Override ++ public int compareTo(Path other) { ++ return delegate.compareTo(fs.unwrap(other)); ++ } ++ ++ @Override ++ public WatchKey register(WatchService watcher, ++ WatchEvent.Kind[] events, ++ WatchEvent.Modifier... modifiers) ++ { ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public WatchKey register(WatchService watcher, ++ WatchEvent.Kind... events) ++ { ++ throw new UnsupportedOperationException(); + } + } +-} ++} +\ No newline at end of file +diff --git a/jdk/test/java/util/zip/ZipFile/TestZipFile.java b/jdk/test/java/util/zip/ZipFile/TestZipFile.java +new file mode 100644 +index 000000000..30bae3bb9 +--- /dev/null ++++ b/jdk/test/java/util/zip/ZipFile/TestZipFile.java +@@ -0,0 +1,375 @@ ++/* ++ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2023, Huawei Technologies Co., Ltd. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++ ++/* ++ * @test ++ * @bug 8142508 ++ * @summary Tests various ZipFile apis ++ * @run main/manual TestZipFile ++ */ ++ ++import java.io.*; ++import java.lang.reflect.Method; ++import java.nio.file.*; ++import java.nio.file.attribute.*; ++import java.util.*; ++import java.util.concurrent.*; ++import java.util.zip.*; ++ ++public class TestZipFile { ++ ++ private static Random r = new Random(); ++ private static int N = 50; ++ private static int NN = 10; ++ private static int ENUM = 10000; ++ private static int ESZ = 10000; ++ private static ExecutorService executor = Executors.newFixedThreadPool(20); ++ private static Set paths = new HashSet<>(); ++ ++ static void realMain (String[] args) throws Throwable { ++ ++ try { ++ for (int i = 0; i < N; i++) { ++ test(r.nextInt(ENUM), r.nextInt(ESZ), false, true); ++ test(r.nextInt(ENUM), r.nextInt(ESZ), true, true); ++ } ++ ++ for (int i = 0; i < NN; i++) { ++ test(r.nextInt(ENUM), 100000 + r.nextInt(ESZ), false, true); ++ test(r.nextInt(ENUM), 100000 + r.nextInt(ESZ), true, true); ++ testCachedDelete(); ++ testCachedOverwrite(); ++ //test(r.nextInt(ENUM), r.nextInt(ESZ), false, true); ++ } ++ ++ test(70000, 1000, false, true); // > 65536 entry number; ++ testDelete(); // OPEN_DELETE ++ ++ executor.shutdown(); ++ executor.awaitTermination(10, TimeUnit.MINUTES); ++ } finally { ++ for (Path path : paths) { ++ Files.deleteIfExists(path); ++ } ++ } ++ } ++ ++ static void test(int numEntry, int szMax, boolean addPrefix, boolean cleanOld) { ++ String name = "zftest" + r.nextInt() + ".zip"; ++ Zip zip = new Zip(name, numEntry, szMax, addPrefix, cleanOld); ++ for (int i = 0; i < NN; i++) { ++ executor.submit(() -> doTest(zip)); ++ } ++ } ++ ++ // test scenario: ++ // (1) open the ZipFile(zip) with OPEN_READ | OPEN_DELETE ++ // (2) test the ZipFile works correctly ++ // (3) check the zip is deleted after ZipFile gets closed ++ static void testDelete() throws Throwable { ++ String name = "zftest" + r.nextInt() + ".zip"; ++ Zip zip = new Zip(name, r.nextInt(ENUM), r.nextInt(ESZ), false, true); ++ try (ZipFile zf = new ZipFile(new File(zip.name), ++ ZipFile.OPEN_READ | ZipFile.OPEN_DELETE )) ++ { ++ doTest0(zip, zf); ++ } ++ Path p = Paths.get(name); ++ if (Files.exists(p)) { ++ fail("Failed to delete " + name + " with OPEN_DELETE"); ++ } ++ } ++ ++ // test scenario: ++ // (1) keep a ZipFile(zip1) alive (in ZipFile's cache), dont close it ++ // (2) delete zip1 and create zip2 with the same name the zip1 with zip2 ++ // (3) zip1 tests should fail, but no crash ++ // (4) zip2 tasks should all get zip2, then pass normal testing. ++ static void testCachedDelete() throws Throwable { ++ String name = "zftest" + r.nextInt() + ".zip"; ++ Zip zip1 = new Zip(name, r.nextInt(ENUM), r.nextInt(ESZ), false, true); ++ ++ try (ZipFile zf = new ZipFile(zip1.name)) { ++ for (int i = 0; i < NN; i++) { ++ executor.submit(() -> verifyNoCrash(zip1)); ++ } ++ // delete the "zip1" and create a new one to test ++ Zip zip2 = new Zip(name, r.nextInt(ENUM), r.nextInt(ESZ), false, true); ++ /* ++ System.out.println("========================================"); ++ System.out.printf(" zip1=%s, mt=%d, enum=%d%n ->attrs=[key=%s, sz=%d, mt=%d]%n", ++ zip1.name, zip1.lastModified, zip1.entries.size(), ++ zip1.attrs.fileKey(), zip1.attrs.size(), zip1.attrs.lastModifiedTime().toMillis()); ++ System.out.printf(" zip2=%s, mt=%d, enum=%d%n ->attrs=[key=%s, sz=%d, mt=%d]%n", ++ zip2.name, zip2.lastModified, zip2.entries.size(), ++ zip2.attrs.fileKey(), zip2.attrs.size(), zip2.attrs.lastModifiedTime().toMillis()); ++ */ ++ for (int i = 0; i < NN; i++) { ++ executor.submit(() -> doTest(zip2)); ++ } ++ } ++ } ++ ++ // overwrite the "zip1" and create a new one to test. So the two zip files ++ // have the same fileKey, but probably different lastModified() ++ static void testCachedOverwrite() throws Throwable { ++ String name = "zftest" + r.nextInt() + ".zip"; ++ Zip zip1 = new Zip(name, r.nextInt(ENUM), r.nextInt(ESZ), false, true); ++ try (ZipFile zf = new ZipFile(zip1.name)) { ++ for (int i = 0; i < NN; i++) { ++ executor.submit(() -> verifyNoCrash(zip1)); ++ } ++ // overwrite the "zip1" with new contents ++ Zip zip2 = new Zip(name, r.nextInt(ENUM), r.nextInt(ESZ), false, false); ++ for (int i = 0; i < NN; i++) { ++ executor.submit(() -> doTest(zip2)); ++ } ++ } ++ } ++ ++ // This method is used to replace the InputStream.readAllBytes method(need JDK version >= 9). ++ public static byte[] readAllBytes(InputStream inputStream) throws IOException { ++ byte[] buffer = new byte[1024]; ++ int len; ++ try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { ++ while ((len = inputStream.read(buffer)) > -1) { ++ outputStream.write(buffer, 0, len); ++ } ++ return outputStream.toByteArray(); ++ } catch (IOException e) { ++ throw e; ++ } ++ } ++ ++ // just check the entries and contents. since the file has been either overwritten ++ // or deleted/rewritten, we only care if it crahes or not. ++ static void verifyNoCrash(Zip zip) throws RuntimeException { ++ try (ZipFile zf = new ZipFile(zip.name)) { ++ List zlist = new ArrayList(zip.entries.keySet()); ++ String[] elist = zf.stream().map( e -> e.getName()).toArray(String[]::new); ++ if (!Arrays.equals(elist, ++ zlist.stream().map( e -> e.getName()).toArray(String[]::new))) ++ { ++ //System.out.printf("++++++ LIST NG [%s] entries.len=%d, expected=%d+++++++%n", ++ // zf.getName(), elist.length, zlist.size()); ++ return; ++ } ++ for (ZipEntry ze : zlist) { ++ byte[] zdata = zip.entries.get(ze); ++ ZipEntry e = zf.getEntry(ze.getName()); ++ if (e != null) { ++ checkEqual(e, ze); ++ if (!e.isDirectory()) { ++ // check with readAllBytes ++ try (InputStream is = zf.getInputStream(e)) { ++ if (!Arrays.equals(zdata, readAllBytes(is))) { ++ //System.out.printf("++++++ BYTES NG [%s]/[%s] ++++++++%n", ++ // zf.getName(), ze.getName()); ++ } ++ } ++ } ++ } ++ } ++ } catch (Throwable t) { ++ // t.printStackTrace(); ++ // fail(t.toString()); ++ } ++ } ++ ++ static void checkEqual(ZipEntry x, ZipEntry y) { ++ if (x.getName().equals(y.getName()) && ++ x.isDirectory() == y.isDirectory() && ++ x.getMethod() == y.getMethod() && ++ (x.getTime() / 2000) == y.getTime() / 2000 && ++ x.getSize() == y.getSize() && ++ x.getCompressedSize() == y.getCompressedSize() && ++ x.getCrc() == y.getCrc() && ++ x.getComment().equals(y.getComment()) ++ ) { ++ pass(); ++ } else { ++ fail(x + " not equal to " + y); ++ System.out.printf(" %s %s%n", x.getName(), y.getName()); ++ System.out.printf(" %d %d%n", x.getMethod(), y.getMethod()); ++ System.out.printf(" %d %d%n", x.getTime(), y.getTime()); ++ System.out.printf(" %d %d%n", x.getSize(), y.getSize()); ++ System.out.printf(" %d %d%n", x.getCompressedSize(), y.getCompressedSize()); ++ System.out.printf(" %d %d%n", x.getCrc(), y.getCrc()); ++ System.out.println("-----------------"); ++ } ++ } ++ ++ static void doTest(Zip zip) throws RuntimeException { ++ //Thread me = Thread.currentThread(); ++ try (ZipFile zf = new ZipFile(zip.name)) { ++ doTest0(zip, zf); ++ } catch (Throwable t) { ++ throw new RuntimeException(t); ++ } ++ } ++ ++ static void doTest0(Zip zip, ZipFile zf) throws Throwable { ++ List list = new ArrayList(zip.entries.keySet()); ++ // (1) check entry list, in expected order ++ if (!check(Arrays.equals( ++ list.stream().map( e -> e.getName()).toArray(String[]::new), ++ zf.stream().map( e -> e.getName()).toArray(String[]::new)))) { ++ return; ++ } ++ // (2) shuffle, and check each entry and its bytes ++ Collections.shuffle(list); ++ for (ZipEntry ze : list) { ++ byte[] data = zip.entries.get(ze); ++ ZipEntry e = zf.getEntry(ze.getName()); ++ checkEqual(e, ze); ++ if (!e.isDirectory()) { ++ // check with readAllBytes ++ try (InputStream is = zf.getInputStream(e)) { ++ check(Arrays.equals(data, readAllBytes(is))); ++ } ++ // check with smaller sized buf ++ try (InputStream is = zf.getInputStream(e)) { ++ byte[] buf = new byte[(int)e.getSize()]; ++ int sz = r.nextInt((int)e.getSize()/4 + 1) + 1; ++ int off = 0; ++ int n; ++ while ((n = is.read(buf, off, buf.length - off)) > 0) { ++ off += n; ++ } ++ check(is.read() == -1); ++ check(Arrays.equals(data, buf)); ++ } ++ } ++ } ++ // (3) check getMetaInfEntryNames ++ String[] metas = list.stream() ++ .map( e -> e.getName()) ++ .filter( s -> s.startsWith("META-INF/")) ++ .sorted() ++ .toArray(String[]::new); ++ if (metas.length > 0) { ++ // meta-inf entries ++ Method getMetas = ZipFile.class.getDeclaredMethod("getMetaInfEntryNames"); ++ getMetas.setAccessible(true); ++ String[] names = (String[])getMetas.invoke(zf); ++ if (names == null) { ++ fail("Failed to get metanames from " + zf); ++ } else { ++ Arrays.sort(names); ++ check(Arrays.equals(names, metas)); ++ } ++ } ++ } ++ ++ private static class Zip { ++ String name; ++ Map entries; ++ BasicFileAttributes attrs; ++ long lastModified; ++ ++ Zip(String name, int num, int szMax, boolean prefix, boolean clean) { ++ this.name = name; ++ entries = new LinkedHashMap<>(num); ++ try { ++ Path p = Paths.get(name); ++ if (clean) { ++ Files.deleteIfExists(p); ++ } ++ paths.add(p); ++ } catch (Exception x) { ++ throw (RuntimeException)x; ++ } ++ ++ try (FileOutputStream fos = new FileOutputStream(name); ++ BufferedOutputStream bos = new BufferedOutputStream(fos); ++ ZipOutputStream zos = new ZipOutputStream(bos)) ++ { ++ if (prefix) { ++ byte[] bytes = new byte[r.nextInt(1000)]; ++ r.nextBytes(bytes); ++ bos.write(bytes); ++ } ++ CRC32 crc = new CRC32(); ++ for (int i = 0; i < num; i++) { ++ String ename = "entry-" + i + "-name-" + r.nextLong(); ++ ZipEntry ze = new ZipEntry(ename); ++ int method = r.nextBoolean() ? ZipEntry.STORED : ZipEntry.DEFLATED; ++ writeEntry(zos, crc, ze, ZipEntry.STORED, szMax); ++ } ++ // add some manifest entries ++ for (int i = 0; i < r.nextInt(20); i++) { ++ String meta = "META-INF/" + "entry-" + i + "-metainf-" + r.nextLong(); ++ ZipEntry ze = new ZipEntry(meta); ++ writeEntry(zos, crc, ze, ZipEntry.STORED, szMax); ++ } ++ } catch (Exception x) { ++ throw (RuntimeException)x; ++ } ++ try { ++ this.attrs = Files.readAttributes(Paths.get(name), BasicFileAttributes.class); ++ this.lastModified = new File(name).lastModified(); ++ } catch (Exception x) { ++ throw (RuntimeException)x; ++ } ++ } ++ ++ private void writeEntry(ZipOutputStream zos, CRC32 crc, ++ ZipEntry ze, int method, int szMax) ++ throws IOException ++ { ++ ze.setMethod(method); ++ byte[] data = new byte[r.nextInt(szMax + 1)]; ++ r.nextBytes(data); ++ if (method == ZipEntry.STORED) { // must set size/csize/crc ++ ze.setSize(data.length); ++ ze.setCompressedSize(data.length); ++ crc.reset(); ++ crc.update(data); ++ ze.setCrc(crc.getValue()); ++ } ++ ze.setTime(System.currentTimeMillis()); ++ ze.setComment(ze.getName()); ++ zos.putNextEntry(ze); ++ zos.write(data); ++ zos.closeEntry(); ++ entries.put(ze, data); ++ } ++ } ++ ++ //--------------------- Infrastructure --------------------------- ++ static volatile int passed = 0, failed = 0; ++ static void pass() {passed++;} ++ static void pass(String msg) {System.out.println(msg); passed++;} ++ static void fail() {failed++; Thread.dumpStack();} ++ static void fail(String msg) {System.out.println(msg); fail();} ++ static void unexpected(Throwable t) {failed++; t.printStackTrace();} ++ static void unexpected(Throwable t, String msg) { ++ System.out.println(msg); failed++; t.printStackTrace();} ++ static boolean check(boolean cond) {if (cond) pass(); else fail(); return cond;} ++ ++ public static void main(String[] args) throws Throwable { ++ try {realMain(args);} catch (Throwable t) {unexpected(t);} ++ System.out.println("\nPassed = " + passed + " failed = " + failed); ++ if (failed > 0) throw new AssertionError("Some tests failed");} ++} +\ No newline at end of file +diff --git a/jdk/test/javax/imageio/plugins/png/ItxtUtf8Test.java b/jdk/test/javax/imageio/plugins/png/ItxtUtf8Test.java +index 89853c639..74938bcda 100644 +--- a/jdk/test/javax/imageio/plugins/png/ItxtUtf8Test.java ++++ b/jdk/test/javax/imageio/plugins/png/ItxtUtf8Test.java +@@ -30,7 +30,7 @@ + * + * @run main ItxtUtf8Test + * +- * @run main/othervm/timeout=10 -Xmx2m ItxtUtf8Test truncate ++ * @run main/othervm/timeout=10 -Xmx4m ItxtUtf8Test truncate + */ + + import java.awt.image.BufferedImage; +-- +2.22.0 + diff --git a/add-8146431-j.u.z.ZipFile.getEntry-throws-AIOOBE.patch b/add-8146431-j.u.z.ZipFile.getEntry-throws-AIOOBE.patch new file mode 100644 index 0000000..4c466e9 --- /dev/null +++ b/add-8146431-j.u.z.ZipFile.getEntry-throws-AIOOBE.patch @@ -0,0 +1,52 @@ +From b6a24b666a1c7536e35afaba4057cc8eac6fe48f Mon Sep 17 00:00:00 2001 +Date: Thu, 21 Sep 2023 15:25:03 +0800 +Subject: add 8146431-j.u.z.ZipFile.getEntry-throws-AIOOBE + +--- + jdk/src/share/classes/java/util/zip/ZipFile.java | 2 +- + jdk/test/java/util/zip/ZipFile/TestZipFile.java | 9 ++++++++- + 2 files changed, 9 insertions(+), 2 deletions(-) + +diff --git a/jdk/src/share/classes/java/util/zip/ZipFile.java b/jdk/src/share/classes/java/util/zip/ZipFile.java +index 38b642bdc..36135a9c0 100644 +--- a/jdk/src/share/classes/java/util/zip/ZipFile.java ++++ b/jdk/src/share/classes/java/util/zip/ZipFile.java +@@ -1313,7 +1313,7 @@ class ZipFile implements ZipConstants, Closeable { + idx = getEntryNext(idx); + } + /* If not addSlash, or slash is already there, we are done */ +- if (!addSlash || name[name.length - 1] == '/') { ++ if (!addSlash || name.length == 0 || name[name.length - 1] == '/') { + return -1; + } + // Add a slash to the hash code +diff --git a/jdk/test/java/util/zip/ZipFile/TestZipFile.java b/jdk/test/java/util/zip/ZipFile/TestZipFile.java +index 30bae3bb9..773f47558 100644 +--- a/jdk/test/java/util/zip/ZipFile/TestZipFile.java ++++ b/jdk/test/java/util/zip/ZipFile/TestZipFile.java +@@ -24,7 +24,7 @@ + + /* + * @test +- * @bug 8142508 ++ * @bug 8142508 8146431 + * @summary Tests various ZipFile apis + * @run main/manual TestZipFile + */ +@@ -230,6 +230,13 @@ public class TestZipFile { + } + + static void doTest0(Zip zip, ZipFile zf) throws Throwable { ++ // (0) check zero-length entry name, no AIOOBE ++ try { ++ check(zf.getEntry("") == null);; ++ } catch (Throwable t) { ++ unexpected(t); ++ } ++ + List list = new ArrayList(zip.entries.keySet()); + // (1) check entry list, in expected order + if (!check(Arrays.equals( +-- +2.22.0 + diff --git a/add-8147940-Modify-testcase-this-test-does-not-assum.patch b/add-8147940-Modify-testcase-this-test-does-not-assum.patch new file mode 100644 index 0000000..5a45309 --- /dev/null +++ b/add-8147940-Modify-testcase-this-test-does-not-assum.patch @@ -0,0 +1,79 @@ +From 40b88d64b144bd1b94b2a887c132ec88b3a9a39d Mon Sep 17 00:00:00 2001 +Date: Thu, 21 Sep 2023 15:26:13 +0800 +Subject: add 8147940-Modify-testcase-this-test-does-not-assume-th + +--- + ...stG1TraceEagerReclaimHumongousObjects.java | 45 +------------------ + 1 file changed, 1 insertion(+), 44 deletions(-) + +diff --git a/hotspot/test/gc/g1/TestG1TraceEagerReclaimHumongousObjects.java b/hotspot/test/gc/g1/TestG1TraceEagerReclaimHumongousObjects.java +index e653554c9..aca54daf6 100644 +--- a/hotspot/test/gc/g1/TestG1TraceEagerReclaimHumongousObjects.java ++++ b/hotspot/test/gc/g1/TestG1TraceEagerReclaimHumongousObjects.java +@@ -1,5 +1,5 @@ + /* +- * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it +@@ -36,36 +36,6 @@ import java.util.LinkedList; + + public class TestG1TraceEagerReclaimHumongousObjects { + public static void main(String[] args) throws Exception { +- testGCLogs(); +- testHumongousObjectGCLogs(); +- } +- +- private static void testGCLogs() throws Exception { +- +- ProcessBuilder pb = ProcessTools.createJavaProcessBuilder("-XX:+UseG1GC", +- "-Xms128M", +- "-Xmx128M", +- "-Xmn16M", +- "-XX:G1HeapRegionSize=1M", +- "-XX:+PrintGC", +- "-XX:+UnlockExperimentalVMOptions", +- "-XX:G1LogLevel=finest", +- "-XX:+G1TraceEagerReclaimHumongousObjects", +- GCTest.class.getName()); +- +- OutputAnalyzer output = new OutputAnalyzer(pb.start()); +- +- // As G1EagerReclaimHumongousObjects is set(default), below logs should be displayed. +- // And GCTest doesn't have humongous objects, so values should be zero. +- output.shouldContain("[Humongous Reclaim"); +- output.shouldContain("[Humongous Total: 0]"); +- output.shouldContain("[Humongous Candidate: 0]"); +- output.shouldContain("[Humongous Reclaimed: 0]"); +- +- output.shouldHaveExitValue(0); +- } +- +- private static void testHumongousObjectGCLogs() throws Exception { + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder("-XX:+UseG1GC", + "-Xms128M", + "-Xmx128M", +@@ -92,19 +62,6 @@ public class TestG1TraceEagerReclaimHumongousObjects { + output.shouldHaveExitValue(0); + } + +- static class GCTest { +- private static byte[] garbage; +- +- public static void main(String [] args) { +- System.out.println("Creating garbage"); +- // create 128MB of garbage. This should result in at least one GC +- for (int i = 0; i < 1024; i++) { +- garbage = new byte[128 * 1024]; +- } +- System.out.println("Done"); +- } +- } +- + static class GCWithHumongousObjectTest { + + public static final int M = 1024*1024; +-- +2.22.0 + diff --git a/add-8170831-ZipFile-implementation-no-longer-caches-.patch b/add-8170831-ZipFile-implementation-no-longer-caches-.patch new file mode 100644 index 0000000..4a7be7e --- /dev/null +++ b/add-8170831-ZipFile-implementation-no-longer-caches-.patch @@ -0,0 +1,45 @@ +From 8e3e20eef3f18d023ffc327a9fae30c34de84773 Mon Sep 17 00:00:00 2001 +Date: Thu, 21 Sep 2023 15:24:45 +0800 +Subject: add 8170831-ZipFile-implementation-no-longer-caches-the + +--- + jdk/src/share/classes/java/util/zip/ZipFile.java | 9 ++++++++- + 1 file changed, 8 insertions(+), 1 deletion(-) + +diff --git a/jdk/src/share/classes/java/util/zip/ZipFile.java b/jdk/src/share/classes/java/util/zip/ZipFile.java +index b6a6c2a48..38b642bdc 100644 +--- a/jdk/src/share/classes/java/util/zip/ZipFile.java ++++ b/jdk/src/share/classes/java/util/zip/ZipFile.java +@@ -342,7 +342,9 @@ class ZipFile implements ZipConstants, Closeable { + ZipFileInputStream in = null; + synchronized (this) { + ensureOpen(); +- if (!zc.isUTF8() && (entry.flag & EFS) != 0) { ++ if (Objects.equals(lastEntryName, entry.name)) { ++ pos = lastEntryPos; ++ } else if (!zc.isUTF8() && (entry.flag & EFS) != 0) { + pos = zsrc.getEntryPos(zc.getBytesUTF8(entry.name), false); + } else { + pos = zsrc.getEntryPos(zc.getBytes(entry.name), false); +@@ -533,6 +535,9 @@ class ZipFile implements ZipConstants, Closeable { + Spliterator.IMMUTABLE | Spliterator.NONNULL), false); + } + ++ private String lastEntryName; ++ private int lastEntryPos; ++ + /* Checks ensureOpen() before invoke this method */ + private ZipEntry getZipEntry(String name, int pos) { + byte[] cen = zsrc.cen; +@@ -566,6 +571,8 @@ class ZipFile implements ZipConstants, Closeable { + e.comment = zc.toString(cen, start, clen); + } + } ++ lastEntryName = e.name; ++ lastEntryPos = pos; + return e; + } + +-- +2.22.0 + diff --git a/add-8191924-Adjust-DelegatingClassLoader-s-metadata-.patch b/add-8191924-Adjust-DelegatingClassLoader-s-metadata-.patch new file mode 100644 index 0000000..45c2558 --- /dev/null +++ b/add-8191924-Adjust-DelegatingClassLoader-s-metadata-.patch @@ -0,0 +1,100 @@ +From 64123e7422fb9441c8999aaa1ddfdf639295fea1 Mon Sep 17 00:00:00 2001 +Date: Thu, 21 Sep 2023 15:26:33 +0800 +Subject: add 8191924-Adjust-DelegatingClassLoader-s-metadata-spac + +--- + hotspot/src/share/vm/memory/metaspace.cpp | 31 ++++++++++++++++++++--- + 1 file changed, 28 insertions(+), 3 deletions(-) + +diff --git a/hotspot/src/share/vm/memory/metaspace.cpp b/hotspot/src/share/vm/memory/metaspace.cpp +index 07259e649..d65a81267 100644 +--- a/hotspot/src/share/vm/memory/metaspace.cpp ++++ b/hotspot/src/share/vm/memory/metaspace.cpp +@@ -1206,7 +1206,10 @@ class SpaceManager : public CHeapObj { + Mutex* const _lock; + + // Type of metadata allocated. +- Metaspace::MetadataType _mdtype; ++ const Metaspace::MetadataType _mdtype; ++ ++ // Type of metaspace ++ const Metaspace::MetaspaceType _space_type; + + // List of chunks in use by this SpaceManager. Allocations + // are done from the current chunk. The list is used for deallocating +@@ -1218,6 +1221,10 @@ class SpaceManager : public CHeapObj { + // If class space manager, small chunks are unlimited + static uint const _small_chunk_limit; + ++ // Maximum number of specialize chunks to allocate for anonymous and delegating ++ // metadata space to a SpaceManager ++ static uint const _anon_and_delegating_metadata_specialize_chunk_limit; ++ + // Sum of all space in allocated chunks + size_t _allocated_blocks_words; + +@@ -1270,6 +1277,7 @@ class SpaceManager : public CHeapObj { + + public: + SpaceManager(Metaspace::MetadataType mdtype, ++ Metaspace::MetaspaceType space_type, + Mutex* lock); + ~SpaceManager(); + +@@ -1385,6 +1393,7 @@ class SpaceManager : public CHeapObj { + }; + + uint const SpaceManager::_small_chunk_limit = 4; ++uint const SpaceManager::_anon_and_delegating_metadata_specialize_chunk_limit = 4; + + const char* SpaceManager::_expand_lock_name = + "SpaceManager chunk allocation lock"; +@@ -3409,6 +3418,20 @@ size_t SpaceManager::calc_chunk_size(size_t word_size) { + // once a medium chunk has been allocated, no more small + // chunks will be allocated. + size_t chunk_word_size; ++ ++ // Special case for anonymous metadata space. ++ // Anonymous metadata space is usually small, with majority within 1K - 2K range and ++ // rarely about 4K (64-bits JVM). ++ // Instead of jumping to SmallChunk after initial chunk exhausted, keeping allocation ++ // from SpecializeChunk up to _anon_or_delegating_metadata_specialize_chunk_limit (4) ++ // reduces space waste from 60+% to around 30%. ++ if ((_space_type == Metaspace::AnonymousMetaspaceType || _space_type == Metaspace::ReflectionMetaspaceType) && ++ _mdtype == Metaspace::NonClassType && ++ sum_count_in_chunks_in_use(SpecializedIndex) < _anon_and_delegating_metadata_specialize_chunk_limit && ++ word_size + Metachunk::overhead() <= SpecializedChunk) { ++ return SpecializedChunk; ++ } ++ + if (chunks_in_use(MediumIndex) == NULL && + sum_count_in_chunks_in_use(SmallIndex) < _small_chunk_limit) { + chunk_word_size = (size_t) small_chunk_size(); +@@ -3517,8 +3540,10 @@ void SpaceManager::print_on(outputStream* st) const { + } + + SpaceManager::SpaceManager(Metaspace::MetadataType mdtype, ++ Metaspace::MetaspaceType space_type, + Mutex* lock) : + _mdtype(mdtype), ++ _space_type(space_type), + _allocated_blocks_words(0), + _allocated_chunks_words(0), + _allocated_chunks_count(0), +@@ -4926,11 +4951,11 @@ void Metaspace::initialize(Mutex* lock, MetaspaceType type) { + verify_global_initialization(); + + // Allocate SpaceManager for metadata objects. +- _vsm = new SpaceManager(NonClassType, lock); ++ _vsm = new SpaceManager(NonClassType, type, lock); + + if (using_class_space()) { + // Allocate SpaceManager for classes. +- _class_vsm = new SpaceManager(ClassType, lock); ++ _class_vsm = new SpaceManager(ClassType, type, lock); + } else { + _class_vsm = NULL; + } +-- +2.22.0 + diff --git a/add-8198423-Improve-metaspace-chunk-allocation.patch b/add-8198423-Improve-metaspace-chunk-allocation.patch new file mode 100644 index 0000000..4a33cde --- /dev/null +++ b/add-8198423-Improve-metaspace-chunk-allocation.patch @@ -0,0 +1,2770 @@ +From 0ec0400ef6367812d4f256468ea2adda569baaff Mon Sep 17 00:00:00 2001 +Date: Thu, 21 Sep 2023 15:16:04 +0800 +Subject: add 8198423-Improve-metaspace-chunk-allocation + +--- + hotspot/src/share/vm/memory/metachunk.cpp | 93 +- + hotspot/src/share/vm/memory/metachunk.hpp | 93 +- + hotspot/src/share/vm/memory/metaspace.cpp | 1808 ++++++++++++++--- + hotspot/src/share/vm/memory/metaspace.hpp | 18 +- + hotspot/src/share/vm/prims/jni.cpp | 4 +- + .../share/vm/utilities/globalDefinitions.hpp | 1 + + 6 files changed, 1742 insertions(+), 275 deletions(-) + +diff --git a/hotspot/src/share/vm/memory/metachunk.cpp b/hotspot/src/share/vm/memory/metachunk.cpp +index 6cb6625b1..50d0c97eb 100644 +--- a/hotspot/src/share/vm/memory/metachunk.cpp ++++ b/hotspot/src/share/vm/memory/metachunk.cpp +@@ -25,6 +25,7 @@ + #include "precompiled.hpp" + #include "memory/allocation.hpp" + #include "memory/metachunk.hpp" ++#include "utilities/align.hpp" + #include "utilities/copy.hpp" + #include "utilities/debug.hpp" + +@@ -32,8 +33,6 @@ PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + + class VirtualSpaceNode; + +-const size_t metadata_chunk_initialize = 0xf7f7f7f7; +- + size_t Metachunk::object_alignment() { + // Must align pointers and sizes to 8, + // so that 64 bit types get correctly aligned. +@@ -51,21 +50,22 @@ size_t Metachunk::overhead() { + + // Metachunk methods + +-Metachunk::Metachunk(size_t word_size, ++Metachunk::Metachunk(ChunkIndex chunktype, bool is_class, size_t word_size, + VirtualSpaceNode* container) + : Metabase(word_size), ++ _chunk_type(chunktype), ++ _is_class(is_class), ++ _sentinel(CHUNK_SENTINEL), ++ _origin(origin_normal), ++ _use_count(0), + _top(NULL), + _container(container) + { + _top = initial_top(); +-#ifdef ASSERT + set_is_tagged_free(false); +- size_t data_word_size = pointer_delta(end(), +- _top, +- sizeof(MetaWord)); +- Copy::fill_to_words((HeapWord*)_top, +- data_word_size, +- metadata_chunk_initialize); ++#ifdef ASSERT ++ mangle(uninitMetaWordVal); ++ verify(); + #endif + } + +@@ -91,33 +91,63 @@ size_t Metachunk::free_word_size() const { + void Metachunk::print_on(outputStream* st) const { + st->print_cr("Metachunk:" + " bottom " PTR_FORMAT " top " PTR_FORMAT +- " end " PTR_FORMAT " size " SIZE_FORMAT, +- bottom(), _top, end(), word_size()); ++ " end " PTR_FORMAT " size " SIZE_FORMAT " (%s)", ++ bottom(), _top, end(), word_size(), ++ chunk_size_name(get_chunk_type())); + if (Verbose) { + st->print_cr(" used " SIZE_FORMAT " free " SIZE_FORMAT, + used_word_size(), free_word_size()); + } + } + +-#ifndef PRODUCT +-void Metachunk::mangle() { +- // Mangle the payload of the chunk and not the links that ++#ifdef ASSERT ++void Metachunk::mangle(juint word_value) { ++ // Overwrite the payload of the chunk and not the links that + // maintain list of chunks. +- HeapWord* start = (HeapWord*)(bottom() + overhead()); ++ HeapWord* start = (HeapWord*)initial_top(); + size_t size = word_size() - overhead(); +- Copy::fill_to_words(start, size, metadata_chunk_initialize); ++ Copy::fill_to_words(start, size, word_value); + } +-#endif // PRODUCT + + void Metachunk::verify() { +-#ifdef ASSERT +- // Cannot walk through the blocks unless the blocks have +- // headers with sizes. +- assert(bottom() <= _top && +- _top <= (MetaWord*)end(), +- "Chunk has been smashed"); +-#endif +- return; ++ assert(is_valid_sentinel(), err_msg("Chunk " PTR_FORMAT ": sentinel invalid", p2i(this))); ++ const ChunkIndex chunk_type = get_chunk_type(); ++ assert(is_valid_chunktype(chunk_type), err_msg("Chunk " PTR_FORMAT ": Invalid chunk type.", p2i(this))); ++ if (chunk_type != HumongousIndex) { ++ assert(word_size() == get_size_for_nonhumongous_chunktype(chunk_type, is_class()), ++ err_msg("Chunk " PTR_FORMAT ": wordsize " SIZE_FORMAT " does not fit chunk type %s.", ++ p2i(this), word_size(), chunk_size_name(chunk_type))); ++ } ++ assert(is_valid_chunkorigin(get_origin()), err_msg("Chunk " PTR_FORMAT ": Invalid chunk origin.", p2i(this))); ++ assert(bottom() <= _top && _top <= (MetaWord*)end(), ++ err_msg("Chunk " PTR_FORMAT ": Chunk top out of chunk bounds.", p2i(this))); ++ ++ // For non-humongous chunks, starting address shall be aligned ++ // to its chunk size. Humongous chunks start address is ++ // aligned to specialized chunk size. ++ const size_t required_alignment = ++ (chunk_type != HumongousIndex ? word_size() : get_size_for_nonhumongous_chunktype(SpecializedIndex, is_class())) * sizeof(MetaWord); ++ assert(is_aligned((address)this, required_alignment), ++ err_msg("Chunk " PTR_FORMAT ": (size " SIZE_FORMAT ") not aligned to " SIZE_FORMAT ".", ++ p2i(this), word_size() * sizeof(MetaWord), required_alignment)); ++} ++ ++#endif // ASSERT ++ ++// Helper, returns a descriptive name for the given index. ++const char* chunk_size_name(ChunkIndex index) { ++ switch (index) { ++ case SpecializedIndex: ++ return "specialized"; ++ case SmallIndex: ++ return "small"; ++ case MediumIndex: ++ return "medium"; ++ case HumongousIndex: ++ return "humongous"; ++ default: ++ return "Invalid index"; ++ } + } + + /////////////// Unit tests /////////////// +@@ -127,11 +157,16 @@ void Metachunk::verify() { + class TestMetachunk { + public: + static void test() { +- size_t size = 2 * 1024 * 1024; +- void* memory = malloc(size); ++ const ChunkIndex chunk_type = MediumIndex; ++ const bool is_class = false; ++ const size_t word_size = get_size_for_nonhumongous_chunktype(chunk_type, is_class); ++ // Allocate the chunk with correct alignment. ++ void* memory = malloc(word_size * BytesPerWord * 2); + assert(memory != NULL, "Failed to malloc 2MB"); ++ ++ void* p_placement = align_up(memory, word_size * BytesPerWord); + +- Metachunk* metachunk = ::new (memory) Metachunk(size / BytesPerWord, NULL); ++ Metachunk* metachunk = ::new (p_placement) Metachunk(chunk_type, is_class, word_size, NULL); + + assert(metachunk->bottom() == (MetaWord*)metachunk, "assert"); + assert(metachunk->end() == (uintptr_t*)metachunk + metachunk->size(), "assert"); +diff --git a/hotspot/src/share/vm/memory/metachunk.hpp b/hotspot/src/share/vm/memory/metachunk.hpp +index 7889b622c..b679f1b1e 100644 +--- a/hotspot/src/share/vm/memory/metachunk.hpp ++++ b/hotspot/src/share/vm/memory/metachunk.hpp +@@ -94,6 +94,61 @@ class Metabase VALUE_OBJ_CLASS_SPEC { + // | | | | + // +--------------+ <- bottom --+ --+ + ++// ChunkIndex defines the type of chunk. ++// Chunk types differ by size: specialized < small < medium, chunks ++// larger than medium are humongous chunks of varying size. ++enum ChunkIndex { ++ ZeroIndex = 0, ++ SpecializedIndex = ZeroIndex, ++ SmallIndex = SpecializedIndex + 1, ++ MediumIndex = SmallIndex + 1, ++ HumongousIndex = MediumIndex + 1, ++ NumberOfFreeLists = 3, ++ NumberOfInUseLists = 4 ++}; ++ ++// Utility functions. ++size_t get_size_for_nonhumongous_chunktype(ChunkIndex chunk_type, bool is_class); ++ChunkIndex get_chunk_type_by_size(size_t size, bool is_class); ++ ++// Returns a descriptive name for a chunk type. ++const char* chunk_size_name(ChunkIndex index); ++ ++// Verify chunk type. ++inline bool is_valid_chunktype(ChunkIndex index) { ++ return index == SpecializedIndex || index == SmallIndex || ++ index == MediumIndex || index == HumongousIndex; ++} ++ ++inline bool is_valid_nonhumongous_chunktype(ChunkIndex index) { ++ return is_valid_chunktype(index) && index != HumongousIndex; ++} ++ ++enum ChunkOrigin { ++ // Chunk normally born (via take_from_committed) ++ origin_normal = 1, ++ // Chunk was born as padding chunk ++ origin_pad = 2, ++ // Chunk was born as leftover chunk in VirtualSpaceNode::retire ++ origin_leftover = 3, ++ // Chunk was born as result of a merge of smaller chunks ++ origin_merge = 4, ++ // Chunk was born as result of a split of a larger chunk ++ origin_split = 5, ++ ++ origin_minimum = origin_normal, ++ origin_maximum = origin_split, ++ origins_count = origin_maximum + 1 ++}; ++ ++inline bool is_valid_chunkorigin(ChunkOrigin origin) { ++ return origin == origin_normal || ++ origin == origin_pad || ++ origin == origin_leftover || ++ origin == origin_merge || ++ origin == origin_split; ++} ++ + class Metachunk : public Metabase { + friend class TestMetachunk; + // The VirtualSpaceNode containing this chunk. +@@ -102,8 +157,21 @@ class Metachunk : public Metabase { + // Current allocation top. + MetaWord* _top; + +- DEBUG_ONLY(bool _is_tagged_free;) ++ // A 32bit sentinel for debugging purposes. ++ enum { CHUNK_SENTINEL = 0x4d4554EF, // "MET" ++ CHUNK_SENTINEL_INVALID = 0xFEEEEEEF ++ }; ++ ++ uint32_t _sentinel; + ++ const ChunkIndex _chunk_type; ++ const bool _is_class; ++ // Whether the chunk is free (in freelist) or in use by some class loader. ++ bool _is_tagged_free; ++ ++ ChunkOrigin _origin; ++ int _use_count; ++ + MetaWord* initial_top() const { return (MetaWord*)this + overhead(); } + MetaWord* top() const { return _top; } + +@@ -120,7 +188,7 @@ class Metachunk : public Metabase { + // Size of the Metachunk header, including alignment. + static size_t overhead(); + +- Metachunk(size_t word_size , VirtualSpaceNode* container); ++ Metachunk(ChunkIndex chunktype, bool is_class, size_t word_size, VirtualSpaceNode* container); + + MetaWord* allocate(size_t word_size); + +@@ -140,17 +208,28 @@ class Metachunk : public Metabase { + size_t used_word_size() const; + size_t free_word_size() const; + +-#ifdef ASSERT + bool is_tagged_free() { return _is_tagged_free; } + void set_is_tagged_free(bool v) { _is_tagged_free = v; } +-#endif + + bool contains(const void* ptr) { return bottom() <= ptr && ptr < _top; } + +- NOT_PRODUCT(void mangle();) +- + void print_on(outputStream* st) const; +- void verify(); ++ ++ bool is_valid_sentinel() const { return _sentinel == CHUNK_SENTINEL; } ++ void remove_sentinel() { _sentinel = CHUNK_SENTINEL_INVALID; } ++ ++ int get_use_count() const { return _use_count; } ++ void inc_use_count() { _use_count ++; } ++ ++ ChunkOrigin get_origin() const { return _origin; } ++ void set_origin(ChunkOrigin orig) { _origin = orig; } ++ ++ ChunkIndex get_chunk_type() const { return _chunk_type; } ++ bool is_class() const { return _is_class; } ++ ++ DEBUG_ONLY(void mangle(juint word_value);) ++ DEBUG_ONLY(void verify();) ++ + }; + + // Metablock is the unit of allocation from a Chunk. +diff --git a/hotspot/src/share/vm/memory/metaspace.cpp b/hotspot/src/share/vm/memory/metaspace.cpp +index cc5bd4544..07259e649 100644 +--- a/hotspot/src/share/vm/memory/metaspace.cpp ++++ b/hotspot/src/share/vm/memory/metaspace.cpp +@@ -46,6 +46,7 @@ + #include "runtime/orderAccess.inline.hpp" + #include "services/memTracker.hpp" + #include "services/memoryService.hpp" ++#include "utilities/align.hpp" + #include "utilities/copy.hpp" + #include "utilities/debug.hpp" + +@@ -57,6 +58,13 @@ typedef BinaryTreeDictionary > ChunkTreeDictionar + // Set this constant to enable slow integrity checking of the free chunk lists + const bool metaspace_slow_verify = false; + ++// Helper function that does a bunch of checks for a chunk. ++DEBUG_ONLY(static void do_verify_chunk(Metachunk* chunk);) ++ ++// Given a Metachunk, update its in-use information (both in the ++// chunk and the occupancy map). ++static void do_update_in_use_info_for_chunk(Metachunk* chunk, bool inuse); ++ + size_t const allocation_from_dictionary_limit = 4 * K; + + MetaWord* last_allocated = 0; +@@ -64,33 +72,6 @@ MetaWord* last_allocated = 0; + size_t Metaspace::_compressed_class_space_size; + const MetaspaceTracer* Metaspace::_tracer = NULL; + +-// Used in declarations in SpaceManager and ChunkManager +-enum ChunkIndex { +- ZeroIndex = 0, +- SpecializedIndex = ZeroIndex, +- SmallIndex = SpecializedIndex + 1, +- MediumIndex = SmallIndex + 1, +- HumongousIndex = MediumIndex + 1, +- NumberOfFreeLists = 3, +- NumberOfInUseLists = 4 +-}; +- +-// Helper, returns a descriptive name for the given index. +-static const char* chunk_size_name(ChunkIndex index) { +- switch (index) { +- case SpecializedIndex: +- return "specialized"; +- case SmallIndex: +- return "small"; +- case MediumIndex: +- return "medium"; +- case HumongousIndex: +- return "humongous"; +- default: +- return "Invalid index"; +- } +-} +- + enum ChunkSizes { // in words. + ClassSpecializedChunk = 128, + SpecializedChunk = 128, +@@ -100,11 +81,69 @@ enum ChunkSizes { // in words. + MediumChunk = 8 * K + }; + ++// Returns size of this chunk type. ++size_t get_size_for_nonhumongous_chunktype(ChunkIndex chunktype, bool is_class) { ++ assert(is_valid_nonhumongous_chunktype(chunktype), "invalid chunk type."); ++ size_t size = 0; ++ if (is_class) { ++ switch(chunktype) { ++ case SpecializedIndex: size = ClassSpecializedChunk; break; ++ case SmallIndex: size = ClassSmallChunk; break; ++ case MediumIndex: size = ClassMediumChunk; break; ++ default: ++ ShouldNotReachHere(); ++ } ++ } else { ++ switch(chunktype) { ++ case SpecializedIndex: size = SpecializedChunk; break; ++ case SmallIndex: size = SmallChunk; break; ++ case MediumIndex: size = MediumChunk; break; ++ default: ++ ShouldNotReachHere(); ++ } ++ } ++ return size; ++} ++ ++ChunkIndex get_chunk_type_by_size(size_t size, bool is_class) { ++ if (is_class) { ++ if (size == ClassSpecializedChunk) { ++ return SpecializedIndex; ++ } else if (size == ClassSmallChunk) { ++ return SmallIndex; ++ } else if (size == ClassMediumChunk) { ++ return MediumIndex; ++ } else if (size > ClassMediumChunk) { ++ assert(is_aligned(size, ClassSpecializedChunk), "Invalid chunk size"); ++ return HumongousIndex; ++ } ++ } else { ++ if (size == SpecializedChunk) { ++ return SpecializedIndex; ++ } else if (size == SmallChunk) { ++ return SmallIndex; ++ } else if (size == MediumChunk) { ++ return MediumIndex; ++ } else if (size > MediumChunk) { ++ assert(is_aligned(size, SpecializedChunk), "Invalid chunk size"); ++ return HumongousIndex; ++ } ++ } ++ ShouldNotReachHere(); ++ return (ChunkIndex)-1; ++} ++ ++ + static ChunkIndex next_chunk_index(ChunkIndex i) { + assert(i < NumberOfInUseLists, "Out of bound"); + return (ChunkIndex) (i+1); + } + ++static ChunkIndex prev_chunk_index(ChunkIndex i) { ++ assert(i > ZeroIndex, "Out of bound"); ++ return (ChunkIndex) (i-1); ++} ++ + static const char* scale_unit(size_t scale) { + switch(scale) { + case 1: return "BYTES"; +@@ -131,24 +170,33 @@ class ChunkManager : public CHeapObj { + // SpecializedChunk + // SmallChunk + // MediumChunk +- // HumongousChunk + ChunkList _free_chunks[NumberOfFreeLists]; + ++ // Whether or not this is the class chunkmanager. ++ const bool _is_class; ++ ++ // Return non-humongous chunk list by its index. ++ ChunkList* free_chunks(ChunkIndex index); ++ ++ // Returns non-humongous chunk list for the given chunk word size. ++ ChunkList* find_free_chunks_list(size_t word_size); ++ + // HumongousChunk + ChunkTreeDictionary _humongous_dictionary; + +- // ChunkManager in all lists of this type ++ // Returns the humongous chunk dictionary. ++ ChunkTreeDictionary* humongous_dictionary() { ++ return &_humongous_dictionary; ++ } ++ ++ // Size, in metaspace words, of all chunks managed by this ChunkManager + size_t _free_chunks_total; ++ // Number of chunks in this ChunkManager + size_t _free_chunks_count; + +- void dec_free_chunks_total(size_t v) { +- assert(_free_chunks_count > 0 && +- _free_chunks_total > 0, +- "About to go negative"); +- Atomic::add_ptr(-1, &_free_chunks_count); +- jlong minus_v = (jlong) - (jlong) v; +- Atomic::add_ptr(minus_v, &_free_chunks_total); +- } ++ // Update counters after a chunk was added or removed removed. ++ void account_for_added_chunk(const Metachunk* c); ++ void account_for_removed_chunk(const Metachunk* c); + + // Debug support + +@@ -169,6 +217,29 @@ class ChunkManager : public CHeapObj { + } + void verify_free_chunks_count(); + ++ // Given a pointer to a chunk, attempts to merge it with neighboring ++ // free chunks to form a bigger chunk. Returns true if successful. ++ bool attempt_to_coalesce_around_chunk(Metachunk* chunk, ChunkIndex target_chunk_type); ++ ++ // Helper for chunk merging: ++ // Given an address range with 1-n chunks which are all supposed to be ++ // free and hence currently managed by this ChunkManager, remove them ++ // from this ChunkManager and mark them as invalid. ++ // - This does not correct the occupancy map. ++ // - This does not adjust the counters in ChunkManager. ++ // - Does not adjust container count counter in containing VirtualSpaceNode. ++ // Returns number of chunks removed. ++ int remove_chunks_in_area(MetaWord* p, size_t word_size); ++ ++ // Helper for chunk splitting: given a target chunk size and a larger free chunk, ++ // split up the larger chunk into n smaller chunks, at least one of which should be ++ // the target chunk of target chunk size. The smaller chunks, including the target ++ // chunk, are returned to the freelist. The pointer to the target chunk is returned. ++ // Note that this chunk is supposed to be removed from the freelist right away. ++ Metachunk* split_chunk(size_t target_chunk_word_size, Metachunk* chunk); ++ ++ public: ++ + struct ChunkManagerStatistics { + size_t num_by_type[NumberOfFreeLists]; + size_t single_size_by_type[NumberOfFreeLists]; +@@ -181,16 +252,15 @@ class ChunkManager : public CHeapObj { + void get_statistics(ChunkManagerStatistics* stat) const; + static void print_statistics(const ChunkManagerStatistics* stat, outputStream* out, size_t scale); + +- public: + +- ChunkManager(size_t specialized_size, size_t small_size, size_t medium_size) +- : _free_chunks_total(0), _free_chunks_count(0) { +- _free_chunks[SpecializedIndex].set_size(specialized_size); +- _free_chunks[SmallIndex].set_size(small_size); +- _free_chunks[MediumIndex].set_size(medium_size); ++ ChunkManager(bool is_class) ++ : _is_class(is_class), _free_chunks_total(0), _free_chunks_count(0) { ++ _free_chunks[SpecializedIndex].set_size(get_size_for_nonhumongous_chunktype(SpecializedIndex, is_class)); ++ _free_chunks[SmallIndex].set_size(get_size_for_nonhumongous_chunktype(SmallIndex, is_class)); ++ _free_chunks[MediumIndex].set_size(get_size_for_nonhumongous_chunktype(MediumIndex, is_class)); + } + +- // add or delete (return) a chunk to the global freelist. ++ // Add or delete (return) a chunk to the global freelist. + Metachunk* chunk_freelist_allocate(size_t word_size); + + // Map a size to a list index assuming that there are lists +@@ -200,13 +270,24 @@ class ChunkManager : public CHeapObj { + // Map a given index to the chunk size. + size_t size_by_index(ChunkIndex index) const; + +- // Remove the chunk from its freelist. It is +- // expected to be on one of the _free_chunks[] lists. ++ bool is_class() const { return _is_class; } ++ ++ // Convenience accessors. ++ size_t medium_chunk_word_size() const { return size_by_index(MediumIndex); } ++ size_t small_chunk_word_size() const { return size_by_index(SmallIndex); } ++ size_t specialized_chunk_word_size() const { return size_by_index(SpecializedIndex); } ++ ++ // Take a chunk from the ChunkManager. The chunk is expected to be in ++ // the chunk manager (the freelist if non-humongous, the dictionary if ++ // humongous). + void remove_chunk(Metachunk* chunk); + ++ // Return a single chunk of type index to the ChunkManager. ++ void return_single_chunk(ChunkIndex index, Metachunk* chunk); ++ + // Add the simple linked list of chunks to the freelist of chunks + // of type index. +- void return_chunks(ChunkIndex index, Metachunk* chunks); ++ void return_chunk_list(ChunkIndex index, Metachunk* chunk); + + // Total of the space in the free chunks list + size_t free_chunks_total_words(); +@@ -215,19 +296,6 @@ class ChunkManager : public CHeapObj { + // Number of chunks in the free chunks list + size_t free_chunks_count(); + +- void inc_free_chunks_total(size_t v, size_t count = 1) { +- Atomic::add_ptr(count, &_free_chunks_count); +- Atomic::add_ptr(v, &_free_chunks_total); +- } +- ChunkTreeDictionary* humongous_dictionary() { +- return &_humongous_dictionary; +- } +- +- ChunkList* free_chunks(ChunkIndex index); +- +- // Returns the list for the given chunk word size. +- ChunkList* find_free_chunks_list(size_t word_size); +- + // Remove from a list by size. Selects list based on size of chunk. + Metachunk* free_chunks_get(size_t chunk_word_size); + +@@ -329,6 +397,294 @@ class BlockFreelist VALUE_OBJ_CLASS_SPEC { + void print_on(outputStream* st) const; + }; + ++// Helper for Occupancy Bitmap. A type trait to give an all-bits-are-one-unsigned constant. ++template struct all_ones { static const T value; }; ++template <> struct all_ones { static const uint64_t value = 0xFFFFFFFFFFFFFFFFULL; }; ++template <> struct all_ones { static const uint32_t value = 0xFFFFFFFF; }; ++ ++// The OccupancyMap is a bitmap which, for a given VirtualSpaceNode, ++// keeps information about ++// - where a chunk starts ++// - whether a chunk is in-use or free ++// A bit in this bitmap represents one range of memory in the smallest ++// chunk size (SpecializedChunk or ClassSpecializedChunk). ++class OccupancyMap : public CHeapObj { ++ ++ // The address range this map covers. ++ const MetaWord* const _reference_address; ++ const size_t _word_size; ++ ++ // The word size of a specialized chunk, aka the number of words one ++ // bit in this map represents. ++ const size_t _smallest_chunk_word_size; ++ ++ // map data ++ // Data are organized in two bit layers: ++ // The first layer is the chunk-start-map. Here, a bit is set to mark ++ // the corresponding region as the head of a chunk. ++ // The second layer is the in-use-map. Here, a set bit indicates that ++ // the corresponding belongs to a chunk which is in use. ++ uint8_t* _map[2]; ++ ++ enum { layer_chunk_start_map = 0, layer_in_use_map = 1 }; ++ ++ // length, in bytes, of bitmap data ++ size_t _map_size; ++ ++ // Returns true if bit at position pos at bit-layer layer is set. ++ bool get_bit_at_position(unsigned pos, unsigned layer) const { ++ assert(layer == 0 || layer == 1, err_msg("Invalid layer %d", layer)); ++ const unsigned byteoffset = pos / 8; ++ assert(byteoffset < _map_size, ++ err_msg("invalid byte offset (%u), map size is " SIZE_FORMAT ".", byteoffset, _map_size)); ++ const unsigned mask = 1 << (pos % 8); ++ return (_map[layer][byteoffset] & mask) > 0; ++ } ++ ++ // Changes bit at position pos at bit-layer layer to value v. ++ void set_bit_at_position(unsigned pos, unsigned layer, bool v) { ++ assert(layer == 0 || layer == 1, err_msg("Invalid layer %d", layer)); ++ const unsigned byteoffset = pos / 8; ++ assert(byteoffset < _map_size, ++ err_msg("invalid byte offset (%u), map size is " SIZE_FORMAT ".", byteoffset, _map_size)); ++ const unsigned mask = 1 << (pos % 8); ++ if (v) { ++ _map[layer][byteoffset] |= mask; ++ } else { ++ _map[layer][byteoffset] &= ~mask; ++ } ++ } ++ ++ // Optimized case of is_any_bit_set_in_region for 32/64bit aligned access: ++ // pos is 32/64 aligned and num_bits is 32/64. ++ // This is the typical case when coalescing to medium chunks, whose size is ++ // 32 or 64 times the specialized chunk size (depending on class or non class ++ // case), so they occupy 64 bits which should be 64bit aligned, because ++ // chunks are chunk-size aligned. ++ template ++ bool is_any_bit_set_in_region_3264(unsigned pos, unsigned num_bits, unsigned layer) const { ++ assert(_map_size > 0, "not initialized"); ++ assert(layer == 0 || layer == 1, err_msg("Invalid layer %d.", layer)); ++ assert(pos % (sizeof(T) * 8) == 0, err_msg("Bit position must be aligned (%u).", pos)); ++ assert(num_bits == (sizeof(T) * 8), err_msg("Number of bits incorrect (%u).", num_bits)); ++ const size_t byteoffset = pos / 8; ++ assert(byteoffset <= (_map_size - sizeof(T)), ++ err_msg("Invalid byte offset (" SIZE_FORMAT "), map size is " SIZE_FORMAT ".", byteoffset, _map_size)); ++ const T w = *(T*)(_map[layer] + byteoffset); ++ return w > 0 ? true : false; ++ } ++ ++ // Returns true if any bit in region [pos1, pos1 + num_bits) is set in bit-layer layer. ++ bool is_any_bit_set_in_region(unsigned pos, unsigned num_bits, unsigned layer) const { ++ if (pos % 32 == 0 && num_bits == 32) { ++ return is_any_bit_set_in_region_3264(pos, num_bits, layer); ++ } else if (pos % 64 == 0 && num_bits == 64) { ++ return is_any_bit_set_in_region_3264(pos, num_bits, layer); ++ } else { ++ for (unsigned n = 0; n < num_bits; n ++) { ++ if (get_bit_at_position(pos + n, layer)) { ++ return true; ++ } ++ } ++ } ++ return false; ++ } ++ ++ // Returns true if any bit in region [p, p+word_size) is set in bit-layer layer. ++ bool is_any_bit_set_in_region(MetaWord* p, size_t word_size, unsigned layer) const { ++ assert(word_size % _smallest_chunk_word_size == 0, ++ err_msg("Region size " SIZE_FORMAT " not a multiple of smallest chunk size.", word_size)); ++ const unsigned pos = get_bitpos_for_address(p); ++ const unsigned num_bits = (unsigned) (word_size / _smallest_chunk_word_size); ++ return is_any_bit_set_in_region(pos, num_bits, layer); ++ } ++ ++ // Optimized case of set_bits_of_region for 32/64bit aligned access: ++ // pos is 32/64 aligned and num_bits is 32/64. ++ // This is the typical case when coalescing to medium chunks, whose size ++ // is 32 or 64 times the specialized chunk size (depending on class or non ++ // class case), so they occupy 64 bits which should be 64bit aligned, ++ // because chunks are chunk-size aligned. ++ template ++ void set_bits_of_region_T(unsigned pos, unsigned num_bits, unsigned layer, bool v) { ++ assert(pos % (sizeof(T) * 8) == 0, err_msg("Bit position must be aligned to %u (%u).", ++ (unsigned)(sizeof(T) * 8), pos)); ++ assert(num_bits == (sizeof(T) * 8), err_msg("Number of bits incorrect (%u), expected %u.", ++ num_bits, (unsigned)(sizeof(T) * 8))); ++ const size_t byteoffset = pos / 8; ++ assert(byteoffset <= (_map_size - sizeof(T)), ++ err_msg("invalid byte offset (" SIZE_FORMAT "), map size is " SIZE_FORMAT ".", byteoffset, _map_size)); ++ T* const pw = (T*)(_map[layer] + byteoffset); ++ *pw = v ? all_ones::value : (T) 0; ++ } ++ ++ // Set all bits in a region starting at pos to a value. ++ void set_bits_of_region(unsigned pos, unsigned num_bits, unsigned layer, bool v) { ++ assert(_map_size > 0, "not initialized"); ++ assert(layer == 0 || layer == 1, err_msg("Invalid layer %d.", layer)); ++ if (pos % 32 == 0 && num_bits == 32) { ++ set_bits_of_region_T(pos, num_bits, layer, v); ++ } else if (pos % 64 == 0 && num_bits == 64) { ++ set_bits_of_region_T(pos, num_bits, layer, v); ++ } else { ++ for (unsigned n = 0; n < num_bits; n ++) { ++ set_bit_at_position(pos + n, layer, v); ++ } ++ } ++ } ++ ++ // Helper: sets all bits in a region [p, p+word_size). ++ void set_bits_of_region(MetaWord* p, size_t word_size, unsigned layer, bool v) { ++ assert(word_size % _smallest_chunk_word_size == 0, ++ err_msg("Region size " SIZE_FORMAT " not a multiple of smallest chunk size.", word_size)); ++ const unsigned pos = get_bitpos_for_address(p); ++ const unsigned num_bits = (unsigned) (word_size / _smallest_chunk_word_size); ++ set_bits_of_region(pos, num_bits, layer, v); ++ } ++ ++ // Helper: given an address, return the bit position representing that address. ++ unsigned get_bitpos_for_address(const MetaWord* p) const { ++ assert(_reference_address != NULL, "not initialized"); ++ assert(p >= _reference_address && p < _reference_address + _word_size, ++ err_msg("Address %p out of range for occupancy map [%p..%p).", ++ p, _reference_address, _reference_address + _word_size)); ++ assert(is_aligned(p, _smallest_chunk_word_size * sizeof(MetaWord)), ++ err_msg("Address not aligned (%p).", p)); ++ const ptrdiff_t d = (p - _reference_address) / _smallest_chunk_word_size; ++ assert(d >= 0 && (size_t)d < _map_size * 8, "Sanity."); ++ return (unsigned) d; ++ } ++ ++ public: ++ ++ OccupancyMap(const MetaWord* reference_address, size_t word_size, size_t smallest_chunk_word_size) : ++ _reference_address(reference_address), _word_size(word_size), ++ _smallest_chunk_word_size(smallest_chunk_word_size) { ++ assert(reference_address != NULL, "invalid reference address"); ++ assert(is_aligned(reference_address, smallest_chunk_word_size), ++ "Reference address not aligned to smallest chunk size."); ++ assert(is_aligned(word_size, smallest_chunk_word_size), ++ "Word_size shall be a multiple of the smallest chunk size."); ++ // Calculate bitmap size: one bit per smallest_chunk_word_size'd area. ++ size_t num_bits = word_size / smallest_chunk_word_size; ++ _map_size = (num_bits + 7) / 8; ++ assert(_map_size * 8 >= num_bits, "sanity"); ++ _map[0] = (uint8_t*) os::malloc(_map_size, mtInternal); ++ _map[1] = (uint8_t*) os::malloc(_map_size, mtInternal); ++ assert(_map[0] != NULL && _map[1] != NULL, "Occupancy Map: allocation failed."); ++ memset(_map[1], 0, _map_size); ++ memset(_map[0], 0, _map_size); ++ // Sanity test: the first respectively last possible chunk start address in ++ // the covered range shall map to the first and last bit in the bitmap. ++ assert(get_bitpos_for_address(reference_address) == 0, ++ "First chunk address in range must map to fist bit in bitmap."); ++ assert(get_bitpos_for_address(reference_address + word_size - smallest_chunk_word_size) == num_bits - 1, ++ "Last chunk address in range must map to last bit in bitmap."); ++ } ++ ++ ~OccupancyMap() { ++ os::free(_map[0]); ++ os::free(_map[1]); ++ } ++ ++ // Returns true if at address x a chunk is starting. ++ bool chunk_starts_at_address(MetaWord* p) const { ++ const unsigned pos = get_bitpos_for_address(p); ++ return get_bit_at_position(pos, layer_chunk_start_map); ++ } ++ ++ void set_chunk_starts_at_address(MetaWord* p, bool v) { ++ const unsigned pos = get_bitpos_for_address(p); ++ set_bit_at_position(pos, layer_chunk_start_map, v); ++ } ++ ++ // Removes all chunk-start-bits inside a region, typically as a ++ // result of a chunk merge. ++ void wipe_chunk_start_bits_in_region(MetaWord* p, size_t word_size) { ++ set_bits_of_region(p, word_size, layer_chunk_start_map, false); ++ } ++ ++ // Returns true if there are life (in use) chunks in the region limited ++ // by [p, p+word_size). ++ bool is_region_in_use(MetaWord* p, size_t word_size) const { ++ return is_any_bit_set_in_region(p, word_size, layer_in_use_map); ++ } ++ ++ // Marks the region starting at p with the size word_size as in use ++ // or free, depending on v. ++ void set_region_in_use(MetaWord* p, size_t word_size, bool v) { ++ set_bits_of_region(p, word_size, layer_in_use_map, v); ++ } ++ ++#ifdef ASSERT ++ // Verify occupancy map for the address range [from, to). ++ // We need to tell it the address range, because the memory the ++ // occupancy map is covering may not be fully comitted yet. ++ void verify(MetaWord* from, MetaWord* to) { ++ Metachunk* chunk = NULL; ++ int nth_bit_for_chunk = 0; ++ MetaWord* chunk_end = NULL; ++ for (MetaWord* p = from; p < to; p += _smallest_chunk_word_size) { ++ const unsigned pos = get_bitpos_for_address(p); ++ // Check the chunk-starts-info: ++ if (get_bit_at_position(pos, layer_chunk_start_map)) { ++ // Chunk start marked in bitmap. ++ chunk = (Metachunk*) p; ++ if (chunk_end != NULL) { ++ assert(chunk_end == p, err_msg("Unexpected chunk start found at %p (expected " ++ "the next chunk to start at %p).", p, chunk_end)); ++ } ++ assert(chunk->is_valid_sentinel(), err_msg("Invalid chunk at address %p.", p)); ++ if (chunk->get_chunk_type() != HumongousIndex) { ++ guarantee(is_aligned(p, chunk->word_size()), err_msg("Chunk %p not aligned.", p)); ++ } ++ chunk_end = p + chunk->word_size(); ++ nth_bit_for_chunk = 0; ++ assert(chunk_end <= to, "Chunk end overlaps test address range."); ++ } else { ++ // No chunk start marked in bitmap. ++ assert(chunk != NULL, "Chunk should start at start of address range."); ++ assert(p < chunk_end, err_msg("Did not find expected chunk start at %p.", p)); ++ nth_bit_for_chunk ++; ++ } ++ // Check the in-use-info: ++ const bool in_use_bit = get_bit_at_position(pos, layer_in_use_map); ++ if (in_use_bit) { ++ assert(!chunk->is_tagged_free(), err_msg("Chunk %p: marked in-use in map but is free (bit %u).", ++ chunk, nth_bit_for_chunk)); ++ } else { ++ assert(chunk->is_tagged_free(), err_msg("Chunk %p: marked free in map but is in use (bit %u).", ++ chunk, nth_bit_for_chunk)); ++ } ++ } ++ } ++ ++ // Verify that a given chunk is correctly accounted for in the bitmap. ++ void verify_for_chunk(Metachunk* chunk) { ++ assert(chunk_starts_at_address((MetaWord*) chunk), ++ err_msg("No chunk start marked in map for chunk %p.", chunk)); ++ // For chunks larger than the minimal chunk size, no other chunk ++ // must start in its area. ++ if (chunk->word_size() > _smallest_chunk_word_size) { ++ assert(!is_any_bit_set_in_region(((MetaWord*) chunk) + _smallest_chunk_word_size, ++ chunk->word_size() - _smallest_chunk_word_size, layer_chunk_start_map), ++ "No chunk must start within another chunk."); ++ } ++ if (!chunk->is_tagged_free()) { ++ assert(is_region_in_use((MetaWord*)chunk, chunk->word_size()), ++ err_msg("Chunk %p is in use but marked as free in map (%d %d).", ++ chunk, chunk->get_chunk_type(), chunk->get_origin())); ++ } else { ++ assert(!is_region_in_use((MetaWord*)chunk, chunk->word_size()), ++ err_msg("Chunk %p is free but marked as in-use in map (%d %d).", ++ chunk, chunk->get_chunk_type(), chunk->get_origin())); ++ } ++ } ++ ++#endif // ASSERT ++ ++}; ++ + // A VirtualSpaceList node. + class VirtualSpaceNode : public CHeapObj { + friend class VirtualSpaceList; +@@ -336,6 +692,9 @@ class VirtualSpaceNode : public CHeapObj { + // Link to next VirtualSpaceNode + VirtualSpaceNode* _next; + ++ // Whether this node is contained in class or metaspace. ++ const bool _is_class; ++ + // total in the VirtualSpace + MemRegion _reserved; + ReservedSpace _rs; +@@ -344,6 +703,8 @@ class VirtualSpaceNode : public CHeapObj { + // count of chunks contained in this VirtualSpace + uintx _container_count; + ++ OccupancyMap* _occupancy_map; ++ + // Convenience functions to access the _virtual_space + char* low() const { return virtual_space()->low(); } + char* high() const { return virtual_space()->high(); } +@@ -354,16 +715,28 @@ class VirtualSpaceNode : public CHeapObj { + + // Committed but unused space in the virtual space + size_t free_words_in_vs() const; ++ ++ // True if this node belongs to class metaspace. ++ bool is_class() const { return _is_class; } ++ ++ // Helper function for take_from_committed: allocate padding chunks ++ // until top is at the given address. ++ void allocate_padding_chunks_until_top_is_at(MetaWord* target_top); ++ + public: + +- VirtualSpaceNode(size_t byte_size); +- VirtualSpaceNode(ReservedSpace rs) : _top(NULL), _next(NULL), _rs(rs), _container_count(0) {} ++ VirtualSpaceNode(bool is_class, size_t byte_size); ++ VirtualSpaceNode(bool is_class, ReservedSpace rs) : ++ _is_class(is_class), _top(NULL), _next(NULL), _rs(rs), _container_count(0), _occupancy_map(NULL) {} + ~VirtualSpaceNode(); + + // Convenience functions for logical bottom and end + MetaWord* bottom() const { return (MetaWord*) _virtual_space.low(); } + MetaWord* end() const { return (MetaWord*) _virtual_space.high(); } + ++ const OccupancyMap* occupancy_map() const { return _occupancy_map; } ++ OccupancyMap* occupancy_map() { return _occupancy_map; } ++ + bool contains(const void* ptr) { return ptr >= low() && ptr < high(); } + + size_t reserved_words() const { return _virtual_space.reserved_size() / BytesPerWord; } +@@ -424,12 +797,18 @@ class VirtualSpaceNode : public CHeapObj { + // the smallest chunk size. + void retire(ChunkManager* chunk_manager); + +-#ifdef ASSERT +- // Debug support +- void mangle(); +-#endif + + void print_on(outputStream* st) const; ++ void print_map(outputStream* st, bool is_class) const; ++ ++ // Debug support ++ DEBUG_ONLY(void mangle();) ++ // Verify counters, all chunks in this list node and the occupancy map. ++ DEBUG_ONLY(void verify();) ++ // Verify that all free chunks in this node are ideally merged ++ // (there not should be multiple small chunks where a large chunk could exist.) ++ DEBUG_ONLY(void verify_free_chunks_are_ideally_merged();) ++ + }; + + #define assert_is_ptr_aligned(ptr, alignment) \ +@@ -458,7 +837,8 @@ static bool should_commit_large_pages_when_reserving(size_t bytes) { + } + + // byte_size is the size of the associated virtualspace. +-VirtualSpaceNode::VirtualSpaceNode(size_t bytes) : _top(NULL), _next(NULL), _rs(), _container_count(0) { ++VirtualSpaceNode::VirtualSpaceNode(bool is_class, size_t bytes) : ++ _is_class(is_class), _top(NULL), _next(NULL), _rs(), _container_count(0), _occupancy_map(NULL) { + assert_is_size_aligned(bytes, Metaspace::reserve_alignment()); + + #if INCLUDE_CDS +@@ -504,12 +884,14 @@ VirtualSpaceNode::VirtualSpaceNode(size_t bytes) : _top(NULL), _next(NULL), _rs( + } + + void VirtualSpaceNode::purge(ChunkManager* chunk_manager) { ++ DEBUG_ONLY(this->verify();) + Metachunk* chunk = first_chunk(); + Metachunk* invalid_chunk = (Metachunk*) top(); + while (chunk < invalid_chunk ) { + assert(chunk->is_tagged_free(), "Should be tagged free"); + MetaWord* next = ((MetaWord*)chunk) + chunk->word_size(); + chunk_manager->remove_chunk(chunk); ++ chunk->remove_sentinel(); + assert(chunk->next() == NULL && + chunk->prev() == NULL, + "Was not removed from its list"); +@@ -517,6 +899,88 @@ void VirtualSpaceNode::purge(ChunkManager* chunk_manager) { + } + } + ++void VirtualSpaceNode::print_map(outputStream* st, bool is_class) const { ++ ++ if (bottom() == top()) { ++ return; ++ } ++ ++ const size_t spec_chunk_size = is_class ? ClassSpecializedChunk : SpecializedChunk; ++ const size_t small_chunk_size = is_class ? ClassSmallChunk : SmallChunk; ++ const size_t med_chunk_size = is_class ? ClassMediumChunk : MediumChunk; ++ ++ int line_len = 100; ++ const size_t section_len = align_up(spec_chunk_size * line_len, med_chunk_size); ++ line_len = (int)(section_len / spec_chunk_size); ++ ++ static const int NUM_LINES = 4; ++ ++ char* lines[NUM_LINES]; ++ for (int i = 0; i < NUM_LINES; i ++) { ++ lines[i] = (char*)os::malloc(line_len, mtInternal); ++ } ++ int pos = 0; ++ const MetaWord* p = bottom(); ++ const Metachunk* chunk = (const Metachunk*)p; ++ const MetaWord* chunk_end = p + chunk->word_size(); ++ while (p < top()) { ++ if (pos == line_len) { ++ pos = 0; ++ for (int i = 0; i < NUM_LINES; i ++) { ++ st->fill_to(22); ++ st->print_raw(lines[i], line_len); ++ st->cr(); ++ } ++ } ++ if (pos == 0) { ++ st->print(PTR_FORMAT ":", p2i(p)); ++ } ++ if (p == chunk_end) { ++ chunk = (Metachunk*)p; ++ chunk_end = p + chunk->word_size(); ++ } ++ // line 1: chunk starting points (a dot if that area is a chunk start). ++ lines[0][pos] = p == (const MetaWord*)chunk ? '.' : ' '; ++ ++ // Line 2: chunk type (x=spec, s=small, m=medium, h=humongous), uppercase if ++ // chunk is in use. ++ const bool chunk_is_free = ((Metachunk*)chunk)->is_tagged_free(); ++ if (chunk->word_size() == spec_chunk_size) { ++ lines[1][pos] = chunk_is_free ? 'x' : 'X'; ++ } else if (chunk->word_size() == small_chunk_size) { ++ lines[1][pos] = chunk_is_free ? 's' : 'S'; ++ } else if (chunk->word_size() == med_chunk_size) { ++ lines[1][pos] = chunk_is_free ? 'm' : 'M'; ++ } else if (chunk->word_size() > med_chunk_size) { ++ lines[1][pos] = chunk_is_free ? 'h' : 'H'; ++ } else { ++ ShouldNotReachHere(); ++ } ++ ++ // Line 3: chunk origin ++ const ChunkOrigin origin = chunk->get_origin(); ++ lines[2][pos] = origin == origin_normal ? ' ' : '0' + (int) origin; ++ ++ // Line 4: Virgin chunk? Virgin chunks are chunks created as a byproduct of padding or splitting, ++ // but were never used. ++ lines[3][pos] = chunk->get_use_count() > 0 ? ' ' : 'v'; ++ ++ p += spec_chunk_size; ++ pos ++; ++ } ++ if (pos > 0) { ++ for (int i = 0; i < NUM_LINES; i ++) { ++ st->fill_to(22); ++ st->print_raw(lines[i], line_len); ++ st->cr(); ++ } ++ } ++ for (int i = 0; i < NUM_LINES; i ++) { ++ os::free(lines[i]); ++ } ++} ++ ++ + #ifdef ASSERT + uint VirtualSpaceNode::container_count_slow() { + uint count = 0; +@@ -524,6 +988,7 @@ uint VirtualSpaceNode::container_count_slow() { + Metachunk* invalid_chunk = (Metachunk*) top(); + while (chunk < invalid_chunk ) { + MetaWord* next = ((MetaWord*)chunk) + chunk->word_size(); ++ do_verify_chunk(chunk); + // Don't count the chunks on the free lists. Those are + // still part of the VirtualSpaceNode but not currently + // counted. +@@ -536,6 +1001,77 @@ uint VirtualSpaceNode::container_count_slow() { + } + #endif + ++#ifdef ASSERT ++// Verify counters, all chunks in this list node and the occupancy map. ++void VirtualSpaceNode::verify() { ++ uintx num_in_use_chunks = 0; ++ Metachunk* chunk = first_chunk(); ++ Metachunk* invalid_chunk = (Metachunk*) top(); ++ ++ // Iterate the chunks in this node and verify each chunk. ++ while (chunk < invalid_chunk ) { ++ DEBUG_ONLY(do_verify_chunk(chunk);) ++ if (!chunk->is_tagged_free()) { ++ num_in_use_chunks ++; ++ } ++ MetaWord* next = ((MetaWord*)chunk) + chunk->word_size(); ++ chunk = (Metachunk*) next; ++ } ++ assert(_container_count == num_in_use_chunks, err_msg("Container count mismatch (real: " UINTX_FORMAT ++ ", counter: " UINTX_FORMAT ".", num_in_use_chunks, _container_count)); ++ // Also verify the occupancy map. ++ occupancy_map()->verify(this->bottom(), this->top()); ++} ++#endif // ASSERT ++ ++#ifdef ASSERT ++// Verify that all free chunks in this node are ideally merged ++// (there not should be multiple small chunks where a large chunk could exist.) ++void VirtualSpaceNode::verify_free_chunks_are_ideally_merged() { ++ Metachunk* chunk = first_chunk(); ++ Metachunk* invalid_chunk = (Metachunk*) top(); ++ // Shorthands. ++ const size_t size_med = (is_class() ? ClassMediumChunk : MediumChunk) * BytesPerWord; ++ const size_t size_small = (is_class() ? ClassSmallChunk : SmallChunk) * BytesPerWord; ++ int num_free_chunks_since_last_med_boundary = -1; ++ int num_free_chunks_since_last_small_boundary = -1; ++ while (chunk < invalid_chunk ) { ++ // Test for missed chunk merge opportunities: count number of free chunks since last chunk boundary. ++ // Reset the counter when encountering a non-free chunk. ++ if (chunk->get_chunk_type() != HumongousIndex) { ++ if (chunk->is_tagged_free()) { ++ // Count successive free, non-humongous chunks. ++ if (is_aligned(chunk, size_small)) { ++ assert(num_free_chunks_since_last_small_boundary <= 1, ++ err_msg("Missed chunk merge opportunity at " PTR_FORMAT " for chunk size " SIZE_FORMAT_HEX ".", p2i(chunk) - size_small, size_small)); ++ num_free_chunks_since_last_small_boundary = 0; ++ } else if (num_free_chunks_since_last_small_boundary != -1) { ++ num_free_chunks_since_last_small_boundary ++; ++ } ++ if (is_aligned(chunk, size_med)) { ++ assert(num_free_chunks_since_last_med_boundary <= 1, ++ err_msg("Missed chunk merge opportunity at " PTR_FORMAT " for chunk size " SIZE_FORMAT_HEX ".", p2i(chunk) - size_med, size_med)); ++ num_free_chunks_since_last_med_boundary = 0; ++ } else if (num_free_chunks_since_last_med_boundary != -1) { ++ num_free_chunks_since_last_med_boundary ++; ++ } ++ } else { ++ // Encountering a non-free chunk, reset counters. ++ num_free_chunks_since_last_med_boundary = -1; ++ num_free_chunks_since_last_small_boundary = -1; ++ } ++ } else { ++ // One cannot merge areas with a humongous chunk in the middle. Reset counters. ++ num_free_chunks_since_last_med_boundary = -1; ++ num_free_chunks_since_last_small_boundary = -1; ++ } ++ ++ MetaWord* next = ((MetaWord*)chunk) + chunk->word_size(); ++ chunk = (Metachunk*) next; ++ } ++} ++#endif // ASSERT ++ + // List of VirtualSpaces for metadata allocation. + class VirtualSpaceList : public CHeapObj { + friend class VirtualSpaceNode; +@@ -623,6 +1159,7 @@ class VirtualSpaceList : public CHeapObj { + void purge(ChunkManager* chunk_manager); + + void print_on(outputStream* st) const; ++ void print_map(outputStream* st) const; + + class VirtualSpaceListIterator : public StackObj { + VirtualSpaceNode* _virtual_spaces; +@@ -728,8 +1265,6 @@ class SpaceManager : public CHeapObj { + + Mutex* lock() const { return _lock; } + +- const char* chunk_size_name(ChunkIndex index) const; +- + protected: + void initialize(); + +@@ -945,6 +1480,9 @@ void BlockFreelist::print_on(outputStream* st) const { + + VirtualSpaceNode::~VirtualSpaceNode() { + _rs.release(); ++ if (_occupancy_map != NULL) { ++ delete _occupancy_map; ++ } + #ifdef ASSERT + size_t word_size = sizeof(*this) / BytesPerWord; + Copy::fill_to_words((HeapWord*) this, word_size, 0xf1f1f1f1); +@@ -964,10 +1502,124 @@ size_t VirtualSpaceNode::free_words_in_vs() const { + return pointer_delta(end(), top(), sizeof(MetaWord)); + } + ++// Given an address larger than top(), allocate padding chunks until top is at the given address. ++void VirtualSpaceNode::allocate_padding_chunks_until_top_is_at(MetaWord* target_top) { ++ ++ assert(target_top > top(), "Sanity"); ++ ++ // Padding chunks are added to the freelist. ++ ChunkManager* const chunk_manager = Metaspace::get_chunk_manager(this->is_class()); ++ ++ // shorthands ++ const size_t spec_word_size = chunk_manager->specialized_chunk_word_size(); ++ const size_t small_word_size = chunk_manager->small_chunk_word_size(); ++ const size_t med_word_size = chunk_manager->medium_chunk_word_size(); ++ ++ while (top() < target_top) { ++ ++ // We could make this coding more generic, but right now we only deal with two possible chunk sizes ++ // for padding chunks, so it is not worth it. ++ size_t padding_chunk_word_size = small_word_size; ++ if (is_aligned(top(), small_word_size * sizeof(MetaWord)) == false) { ++ assert_is_ptr_aligned(top(), spec_word_size * sizeof(MetaWord)); // Should always hold true. ++ padding_chunk_word_size = spec_word_size; ++ } ++ MetaWord* here = top(); ++ assert_is_ptr_aligned(here, padding_chunk_word_size * sizeof(MetaWord)); ++ inc_top(padding_chunk_word_size); ++ ++ // Create new padding chunk. ++ ChunkIndex padding_chunk_type = get_chunk_type_by_size(padding_chunk_word_size, is_class()); ++ assert(padding_chunk_type == SpecializedIndex || padding_chunk_type == SmallIndex, "sanity"); ++ ++ Metachunk* const padding_chunk = ++ ::new (here) Metachunk(padding_chunk_type, is_class(), padding_chunk_word_size, this); ++ assert(padding_chunk == (Metachunk*)here, "Sanity"); ++ DEBUG_ONLY(padding_chunk->set_origin(origin_pad);) ++ if (TraceMetadataChunkAllocation) { ++ gclog_or_tty->print_cr("Created padding chunk in %s at " ++ PTR_FORMAT ", size " SIZE_FORMAT_HEX ".", ++ (is_class() ? "class space " : "metaspace"), ++ p2i(padding_chunk), padding_chunk->word_size() * sizeof(MetaWord)); ++ } ++ ++ // Mark chunk start in occupancy map. ++ occupancy_map()->set_chunk_starts_at_address((MetaWord*)padding_chunk, true); ++ ++ // Chunks are born as in-use (see MetaChunk ctor). So, before returning ++ // the padding chunk to its chunk manager, mark it as in use (ChunkManager ++ // will assert that). ++ do_update_in_use_info_for_chunk(padding_chunk, true); ++ ++ // Return Chunk to freelist. ++ inc_container_count(); ++ chunk_manager->return_single_chunk(padding_chunk_type, padding_chunk); ++ // Please note: at this point, ChunkManager::return_single_chunk() ++ // may already have merged the padding chunk with neighboring chunks, so ++ // it may have vanished at this point. Do not reference the padding ++ // chunk beyond this point. ++ } ++ ++ assert(top() == target_top, "Sanity"); ++ ++} // allocate_padding_chunks_until_top_is_at() ++ + // Allocates the chunk from the virtual space only. + // This interface is also used internally for debugging. Not all + // chunks removed here are necessarily used for allocation. + Metachunk* VirtualSpaceNode::take_from_committed(size_t chunk_word_size) { ++ // Non-humongous chunks are to be allocated aligned to their chunk ++ // size. So, start addresses of medium chunks are aligned to medium ++ // chunk size, those of small chunks to small chunk size and so ++ // forth. This facilitates merging of free chunks and reduces ++ // fragmentation. Chunk sizes are spec < small < medium, with each ++ // larger chunk size being a multiple of the next smaller chunk ++ // size. ++ // Because of this alignment, me may need to create a number of padding ++ // chunks. These chunks are created and added to the freelist. ++ ++ // The chunk manager to which we will give our padding chunks. ++ ChunkManager* const chunk_manager = Metaspace::get_chunk_manager(this->is_class()); ++ ++ // shorthands ++ const size_t spec_word_size = chunk_manager->specialized_chunk_word_size(); ++ const size_t small_word_size = chunk_manager->small_chunk_word_size(); ++ const size_t med_word_size = chunk_manager->medium_chunk_word_size(); ++ ++ assert(chunk_word_size == spec_word_size || chunk_word_size == small_word_size || ++ chunk_word_size >= med_word_size, "Invalid chunk size requested."); ++ ++ // Chunk alignment (in bytes) == chunk size unless humongous. ++ // Humongous chunks are aligned to the smallest chunk size (spec). ++ const size_t required_chunk_alignment = (chunk_word_size > med_word_size ? ++ spec_word_size : chunk_word_size) * sizeof(MetaWord); ++ ++ // Do we have enough space to create the requested chunk plus ++ // any padding chunks needed? ++ MetaWord* const next_aligned = ++ static_cast(align_up(top(), required_chunk_alignment)); ++ if (!is_available((next_aligned - top()) + chunk_word_size)) { ++ return NULL; ++ } ++ ++ // Before allocating the requested chunk, allocate padding chunks if necessary. ++ // We only need to do this for small or medium chunks: specialized chunks are the ++ // smallest size, hence always aligned. Homungous chunks are allocated unaligned ++ // (implicitly, also aligned to smallest chunk size). ++ if ((chunk_word_size == med_word_size || chunk_word_size == small_word_size) && next_aligned > top()) { ++ if (TraceMetadataChunkAllocation) { ++ gclog_or_tty->print_cr("Creating padding chunks in %s between %p and %p...", ++ (is_class() ? "class space " : "metaspace"), ++ top(), next_aligned); ++ } ++ allocate_padding_chunks_until_top_is_at(next_aligned); ++ // Now, top should be aligned correctly. ++ assert_is_ptr_aligned(top(), required_chunk_alignment); ++ } ++ ++ // Now, top should be aligned correctly. ++ assert_is_ptr_aligned(top(), required_chunk_alignment); ++ + // Bottom of the new chunk + MetaWord* chunk_limit = top(); + assert(chunk_limit != NULL, "Not safe to call this method"); +@@ -991,7 +1643,20 @@ Metachunk* VirtualSpaceNode::take_from_committed(size_t chunk_word_size) { + inc_top(chunk_word_size); + + // Initialize the chunk +- Metachunk* result = ::new (chunk_limit) Metachunk(chunk_word_size, this); ++ ChunkIndex chunk_type = get_chunk_type_by_size(chunk_word_size, is_class()); ++ Metachunk* result = ::new (chunk_limit) Metachunk(chunk_type, is_class(), chunk_word_size, this); ++ assert(result == (Metachunk*)chunk_limit, "Sanity"); ++ occupancy_map()->set_chunk_starts_at_address((MetaWord*)result, true); ++ do_update_in_use_info_for_chunk(result, true); ++ ++ inc_container_count(); ++ ++ DEBUG_ONLY(chunk_manager->locked_verify()); ++ DEBUG_ONLY(this->verify()); ++ DEBUG_ONLY(do_verify_chunk(result)); ++ ++ result->inc_use_count(); ++ + return result; + } + +@@ -1010,6 +1675,16 @@ bool VirtualSpaceNode::expand_by(size_t min_words, size_t preferred_words) { + size_t commit = MIN2(preferred_bytes, uncommitted); + bool result = virtual_space()->expand_by(commit, false); + ++ if (TraceMetadataChunkAllocation) { ++ if (result) { ++ gclog_or_tty->print_cr("Expanded %s virtual space list node by " SIZE_FORMAT " words.", ++ (is_class() ? "class" : "non-class"), commit); ++ } else { ++ gclog_or_tty->print_cr("Failed to expand %s virtual space list node by " SIZE_FORMAT " words.", ++ (is_class() ? "class" : "non-class"), commit); ++ } ++ } ++ + assert(result, "Failed to commit memory"); + + return result; +@@ -1018,9 +1693,6 @@ bool VirtualSpaceNode::expand_by(size_t min_words, size_t preferred_words) { + Metachunk* VirtualSpaceNode::get_chunk_vs(size_t chunk_word_size) { + assert_lock_strong(SpaceManager::expand_lock()); + Metachunk* result = take_from_committed(chunk_word_size); +- if (result != NULL) { +- inc_container_count(); +- } + return result; + } + +@@ -1060,6 +1732,10 @@ bool VirtualSpaceNode::initialize() { + _rs.size() / BytesPerWord)); + } + ++ // Initialize Occupancy Map. ++ const size_t smallest_chunk_size = is_class() ? ClassSpecializedChunk : SpecializedChunk; ++ _occupancy_map = new OccupancyMap(bottom(), reserved_words(), smallest_chunk_size); ++ + return result; + } + +@@ -1140,8 +1816,140 @@ void ChunkManager::remove_chunk(Metachunk* chunk) { + humongous_dictionary()->remove_chunk(chunk); + } + +- // Chunk is being removed from the chunks free list. +- dec_free_chunks_total(chunk->word_size()); ++ // Chunk has been removed from the chunks free list, update counters. ++ account_for_removed_chunk(chunk); ++} ++ ++bool ChunkManager::attempt_to_coalesce_around_chunk(Metachunk* chunk, ChunkIndex target_chunk_type) { ++ assert_lock_strong(SpaceManager::expand_lock()); ++ assert(chunk != NULL, "invalid chunk pointer"); ++ // Check for valid merge combinations. ++ assert((chunk->get_chunk_type() == SpecializedIndex && ++ (target_chunk_type == SmallIndex || target_chunk_type == MediumIndex)) || ++ (chunk->get_chunk_type() == SmallIndex && target_chunk_type == MediumIndex), ++ "Invalid chunk merge combination."); ++ ++ const size_t target_chunk_word_size = ++ get_size_for_nonhumongous_chunktype(target_chunk_type, this->is_class()); ++ ++ // [ prospective merge region ) ++ MetaWord* const p_merge_region_start = ++ (MetaWord*) align_down(chunk, target_chunk_word_size * sizeof(MetaWord)); ++ MetaWord* const p_merge_region_end = ++ p_merge_region_start + target_chunk_word_size; ++ ++ // We need the VirtualSpaceNode containing this chunk and its occupancy map. ++ VirtualSpaceNode* const vsn = chunk->container(); ++ OccupancyMap* const ocmap = vsn->occupancy_map(); ++ ++ // The prospective chunk merge range must be completely contained by the ++ // committed range of the virtual space node. ++ if (p_merge_region_start < vsn->bottom() || p_merge_region_end > vsn->top()) { ++ return false; ++ } ++ ++ // Only attempt to merge this range if at its start a chunk starts and at its end ++ // a chunk ends. If a chunk (can only be humongous) straddles either start or end ++ // of that range, we cannot merge. ++ if (!ocmap->chunk_starts_at_address(p_merge_region_start)) { ++ return false; ++ } ++ if (p_merge_region_end < vsn->top() && ++ !ocmap->chunk_starts_at_address(p_merge_region_end)) { ++ return false; ++ } ++ ++ // Now check if the prospective merge area contains live chunks. If it does we cannot merge. ++ if (ocmap->is_region_in_use(p_merge_region_start, target_chunk_word_size)) { ++ return false; ++ } ++ ++ // Success! Remove all chunks in this region... ++ if (TraceMetadataChunkAllocation) { ++ gclog_or_tty->print_cr("%s: coalescing chunks in area [%p-%p)...", ++ (is_class() ? "class space" : "metaspace"), ++ p_merge_region_start, p_merge_region_end); ++ } ++ ++ const int num_chunks_removed = ++ remove_chunks_in_area(p_merge_region_start, target_chunk_word_size); ++ ++ // ... and create a single new bigger chunk. ++ Metachunk* const p_new_chunk = ++ ::new (p_merge_region_start) Metachunk(target_chunk_type, is_class(), target_chunk_word_size, vsn); ++ assert(p_new_chunk == (Metachunk*)p_merge_region_start, "Sanity"); ++ p_new_chunk->set_origin(origin_merge); ++ if (TraceMetadataChunkAllocation) { ++ gclog_or_tty->print_cr("%s: created coalesced chunk at %p, size " SIZE_FORMAT_HEX ".", ++ (is_class() ? "class space" : "metaspace"), ++ p_new_chunk, p_new_chunk->word_size() * sizeof(MetaWord)); ++ } ++ ++ // Fix occupancy map: remove old start bits of the small chunks and set new start bit. ++ ocmap->wipe_chunk_start_bits_in_region(p_merge_region_start, target_chunk_word_size); ++ ocmap->set_chunk_starts_at_address(p_merge_region_start, true); ++ ++ // Mark chunk as free. Note: it is not necessary to update the occupancy ++ // map in-use map, because the old chunks were also free, so nothing ++ // should have changed. ++ p_new_chunk->set_is_tagged_free(true); ++ ++ // Add new chunk to its freelist. ++ ChunkList* const list = free_chunks(target_chunk_type); ++ list->return_chunk_at_head(p_new_chunk); ++ ++ // And adjust ChunkManager:: _free_chunks_count (_free_chunks_total ++ // should not have changed, because the size of the space should be the same) ++ _free_chunks_count -= num_chunks_removed; ++ _free_chunks_count ++; ++ ++ // VirtualSpaceNode::container_count does not have to be modified: ++ // it means "number of active (non-free) chunks", so merging free chunks ++ // should not affect that count. ++ ++ // At the end of a chunk merge, run verification tests. ++ DEBUG_ONLY(this->locked_verify()); ++ DEBUG_ONLY(vsn->verify()); ++ ++ return true; ++} ++ ++// Remove all chunks in the given area - the chunks are supposed to be free - ++// from their corresponding freelists. Mark them as invalid. ++// - This does not correct the occupancy map. ++// - This does not adjust the counters in ChunkManager. ++// - Does not adjust container count counter in containing VirtualSpaceNode ++// Returns number of chunks removed. ++int ChunkManager::remove_chunks_in_area(MetaWord* p, size_t word_size) { ++ assert(p != NULL && word_size > 0, "Invalid range."); ++ const size_t smallest_chunk_size = get_size_for_nonhumongous_chunktype(SpecializedIndex, is_class()); ++ assert_is_size_aligned(word_size, smallest_chunk_size); ++ ++ Metachunk* const start = (Metachunk*) p; ++ const Metachunk* const end = (Metachunk*)(p + word_size); ++ Metachunk* cur = start; ++ int num_removed = 0; ++ while (cur < end) { ++ Metachunk* next = (Metachunk*)(((MetaWord*)cur) + cur->word_size()); ++ DEBUG_ONLY(do_verify_chunk(cur)); ++ assert(cur->get_chunk_type() != HumongousIndex, err_msg("Unexpected humongous chunk found at %p.", cur)); ++ assert(cur->is_tagged_free(), err_msg("Chunk expected to be free (%p)", cur)); ++ if (TraceMetadataChunkAllocation) { ++ gclog_or_tty->print_cr("%s: removing chunk %p, size " SIZE_FORMAT_HEX ".", ++ (is_class() ? "class space" : "metaspace"), ++ cur, cur->word_size() * sizeof(MetaWord)); ++ } ++ cur->remove_sentinel(); ++ // Note: cannot call ChunkManager::remove_chunk, because that ++ // modifies the counters in ChunkManager, which we do not want. So ++ // we call remove_chunk on the freelist directly (see also the ++ // splitting function which does the same). ++ ChunkList* const list = free_chunks(list_index(cur->word_size())); ++ list->remove_chunk(cur); ++ num_removed ++; ++ cur = next; ++ } ++ return num_removed; + } + + // Walk the list of VirtualSpaceNodes and delete +@@ -1161,6 +1969,10 @@ void VirtualSpaceList::purge(ChunkManager* chunk_manager) { + // Don't free the current virtual space since it will likely + // be needed soon. + if (vsl->container_count() == 0 && vsl != current_virtual_space()) { ++ if (TraceMetadataChunkAllocation) { ++ gclog_or_tty->print_cr("Purging VirtualSpaceNode " PTR_FORMAT " (capacity: " SIZE_FORMAT ++ ", used: " SIZE_FORMAT ").", p2i(vsl), vsl->capacity_words_in_vs(), vsl->used_words_in_vs()); ++ } + // Unlink it from the list + if (prev_vsl == vsl) { + // This is the case of the current node being the first node. +@@ -1221,17 +2033,24 @@ void VirtualSpaceList::retire_current_virtual_space() { + } + + void VirtualSpaceNode::retire(ChunkManager* chunk_manager) { ++ assert(this->is_class() == chunk_manager->is_class(), "Wrong ChunkManager?"); + for (int i = (int)MediumIndex; i >= (int)ZeroIndex; --i) { + ChunkIndex index = (ChunkIndex)i; +- size_t chunk_size = chunk_manager->free_chunks(index)->size(); ++ size_t chunk_size = chunk_manager->size_by_index(index); + + while (free_words_in_vs() >= chunk_size) { + DEBUG_ONLY(verify_container_count();) + Metachunk* chunk = get_chunk_vs(chunk_size); +- assert(chunk != NULL, "allocation should have been successful"); +- +- chunk_manager->return_chunks(index, chunk); +- chunk_manager->inc_free_chunks_total(chunk_size); ++ // Chunk will be allocated aligned, so allocation may require ++ // additional padding chunks. That may cause above allocation to ++ // fail. Just ignore the failed allocation and continue with the ++ // next smaller chunk size. As the VirtualSpaceNode comitted ++ // size should be a multiple of the smallest chunk size, we ++ // should always be able to fill the VirtualSpace completely. ++ if (chunk == NULL) { ++ break; ++ } ++ chunk_manager->return_single_chunk(index, chunk); + DEBUG_ONLY(verify_container_count();) + } + } +@@ -1259,7 +2078,7 @@ VirtualSpaceList::VirtualSpaceList(ReservedSpace rs) : + _virtual_space_count(0) { + MutexLockerEx cl(SpaceManager::expand_lock(), + Mutex::_no_safepoint_check_flag); +- VirtualSpaceNode* class_entry = new VirtualSpaceNode(rs); ++ VirtualSpaceNode* class_entry = new VirtualSpaceNode(is_class(), rs); + bool succeeded = class_entry->initialize(); + if (succeeded) { + link_vs(class_entry); +@@ -1291,7 +2110,7 @@ bool VirtualSpaceList::create_new_virtual_space(size_t vs_word_size) { + assert_is_size_aligned(vs_byte_size, Metaspace::reserve_alignment()); + + // Allocate the meta virtual space and initialize it. +- VirtualSpaceNode* new_entry = new VirtualSpaceNode(vs_byte_size); ++ VirtualSpaceNode* new_entry = new VirtualSpaceNode(is_class(), vs_byte_size); + if (!new_entry->initialize()) { + delete new_entry; + return false; +@@ -1345,12 +2164,22 @@ bool VirtualSpaceList::expand_by(size_t min_words, size_t preferred_words) { + assert_is_size_aligned(preferred_words, Metaspace::commit_alignment_words()); + assert(min_words <= preferred_words, "Invalid arguments"); + ++ const char* const class_or_not = (is_class() ? "class" : "non-class"); ++ + if (!MetaspaceGC::can_expand(min_words, this->is_class())) { ++ if (TraceMetadataChunkAllocation) { ++ gclog_or_tty->print_cr("Cannot expand %s virtual space list.", ++ class_or_not); ++ } + return false; + } + + size_t allowed_expansion_words = MetaspaceGC::allowed_expansion(); + if (allowed_expansion_words < min_words) { ++ if (TraceMetadataChunkAllocation) { ++ gclog_or_tty->print_cr("Cannot expand %s virtual space list (must try gc first).", ++ class_or_not); ++ } + return false; + } + +@@ -1361,8 +2190,16 @@ bool VirtualSpaceList::expand_by(size_t min_words, size_t preferred_words) { + min_words, + max_expansion_words); + if (vs_expanded) { ++ if (TraceMetadataChunkAllocation) { ++ gclog_or_tty->print_cr("Expanded %s virtual space list.", ++ class_or_not); ++ } + return true; + } ++ if (TraceMetadataChunkAllocation) { ++ gclog_or_tty->print_cr("%s virtual space list: retire current node.", ++ class_or_not); ++ } + retire_current_virtual_space(); + + // Get another virtual space. +@@ -1386,6 +2223,24 @@ bool VirtualSpaceList::expand_by(size_t min_words, size_t preferred_words) { + return false; + } + ++// Given a chunk, calculate the largest possible padding space which ++// could be required when allocating it. ++static size_t largest_possible_padding_size_for_chunk(size_t chunk_word_size, bool is_class) { ++ const ChunkIndex chunk_type = get_chunk_type_by_size(chunk_word_size, is_class); ++ if (chunk_type != HumongousIndex) { ++ // Normal, non-humongous chunks are allocated at chunk size ++ // boundaries, so the largest padding space required would be that ++ // minus the smallest chunk size. ++ const size_t smallest_chunk_size = is_class ? ClassSpecializedChunk : SpecializedChunk; ++ return chunk_word_size - smallest_chunk_size; ++ } else { ++ // Humongous chunks are allocated at smallest-chunksize ++ // boundaries, so there is no padding required. ++ return 0; ++ } ++} ++ ++ + Metachunk* VirtualSpaceList::get_new_chunk(size_t chunk_word_size, size_t suggested_commit_granularity) { + + // Allocate a chunk out of the current virtual space. +@@ -1398,7 +2253,11 @@ Metachunk* VirtualSpaceList::get_new_chunk(size_t chunk_word_size, size_t sugges + // The expand amount is currently only determined by the requested sizes + // and not how much committed memory is left in the current virtual space. + +- size_t min_word_size = align_size_up(chunk_word_size, Metaspace::commit_alignment_words()); ++ // We must have enough space for the requested size and any ++ // additional reqired padding chunks. ++ const size_t size_for_padding = largest_possible_padding_size_for_chunk(chunk_word_size, this->is_class()); ++ ++ size_t min_word_size = align_size_up(chunk_word_size + size_for_padding, Metaspace::commit_alignment_words()); + size_t preferred_word_size = align_size_up(suggested_commit_granularity, Metaspace::commit_alignment_words()); + if (min_word_size >= preferred_word_size) { + // Can happen when humongous chunks are allocated. +@@ -1424,6 +2283,18 @@ void VirtualSpaceList::print_on(outputStream* st) const { + } + } + ++void VirtualSpaceList::print_map(outputStream* st) const { ++ VirtualSpaceNode* list = virtual_space_list(); ++ VirtualSpaceListIterator iter(list); ++ unsigned i = 0; ++ while (iter.repeat()) { ++ st->print_cr("Node %u:", i); ++ VirtualSpaceNode* node = iter.get_next(); ++ node->print_map(st, this->is_class()); ++ i ++; ++ } ++} ++ + // MetaspaceGC methods + + // VM_CollectForMetadataAllocation is the vm operation used to GC. +@@ -1547,6 +2418,10 @@ bool MetaspaceGC::can_expand(size_t word_size, bool is_class) { + if (is_class && Metaspace::using_class_space()) { + size_t class_committed = MetaspaceAux::committed_bytes(Metaspace::ClassType); + if (class_committed + word_size * BytesPerWord > CompressedClassSpaceSize) { ++ if (TraceMetadataChunkAllocation) { ++ gclog_or_tty->print_cr("Cannot expand %s metaspace by " SIZE_FORMAT " words (CompressedClassSpaceSize = " SIZE_FORMAT " words)", ++ (is_class ? "class" : "non-class"), word_size, CompressedClassSpaceSize / sizeof(MetaWord)); ++ } + return false; + } + } +@@ -1554,6 +2429,10 @@ bool MetaspaceGC::can_expand(size_t word_size, bool is_class) { + // Check if the user has imposed a limit on the metaspace memory. + size_t committed_bytes = MetaspaceAux::committed_bytes(); + if (committed_bytes + word_size * BytesPerWord > MaxMetaspaceSize) { ++ if (TraceMetadataChunkAllocation) { ++ gclog_or_tty->print_cr("Cannot expand %s metaspace by " SIZE_FORMAT " words (MaxMetaspaceSize = " SIZE_FORMAT " words)", ++ (is_class ? "class" : "non-class"), word_size, MaxMetaspaceSize / sizeof(MetaWord)); ++ } + return false; + } + +@@ -1571,6 +2450,11 @@ size_t MetaspaceGC::allowed_expansion() { + size_t left_until_max = MaxMetaspaceSize - committed_bytes; + size_t left_until_GC = capacity_until_gc - committed_bytes; + size_t left_to_commit = MIN2(left_until_GC, left_until_max); ++ if (TraceMetadataChunkAllocation) { ++ gclog_or_tty->print_cr("allowed expansion words: " SIZE_FORMAT ++ " (left_until_max: " SIZE_FORMAT ", left_until_GC: " SIZE_FORMAT ".", ++ left_to_commit / BytesPerWord, left_until_max / BytesPerWord, left_until_GC / BytesPerWord); ++ } + + return left_to_commit / BytesPerWord; + } +@@ -1762,6 +2646,25 @@ size_t ChunkManager::free_chunks_total_bytes() { + return free_chunks_total_words() * BytesPerWord; + } + ++// Update internal accounting after a chunk was added ++void ChunkManager::account_for_added_chunk(const Metachunk* c) { ++ assert_lock_strong(SpaceManager::expand_lock()); ++ _free_chunks_count ++; ++ _free_chunks_total += c->word_size(); ++} ++ ++// Update internal accounting after a chunk was removed ++void ChunkManager::account_for_removed_chunk(const Metachunk* c) { ++ assert_lock_strong(SpaceManager::expand_lock()); ++ assert(_free_chunks_count >= 1, ++ err_msg("ChunkManager::_free_chunks_count: about to go negative (" SIZE_FORMAT ").", _free_chunks_count)); ++ assert(_free_chunks_total >= c->word_size(), ++ err_msg("ChunkManager::_free_chunks_total: about to go negative" ++ "(now: " SIZE_FORMAT ", decrement value: " SIZE_FORMAT ").", _free_chunks_total, c->word_size())); ++ _free_chunks_count --; ++ _free_chunks_total -= c->word_size(); ++} ++ + size_t ChunkManager::free_chunks_count() { + #ifdef ASSERT + if (!UseConcMarkSweepGC && !SpaceManager::expand_lock()->is_locked()) { +@@ -1775,6 +2678,22 @@ size_t ChunkManager::free_chunks_count() { + return _free_chunks_count; + } + ++ChunkIndex ChunkManager::list_index(size_t size) { ++ if (size_by_index(SpecializedIndex) == size) { ++ return SpecializedIndex; ++ } ++ if (size_by_index(SmallIndex) == size) { ++ return SmallIndex; ++ } ++ const size_t med_size = size_by_index(MediumIndex); ++ if (med_size == size) { ++ return MediumIndex; ++ } ++ ++ assert(size > med_size, "Not a humongous chunk"); ++ return HumongousIndex; ++} ++ + size_t ChunkManager::size_by_index(ChunkIndex index) const { + index_bounds_check(index); + assert(index != HumongousIndex, "Do not call for humongous chunks."); +@@ -1820,6 +2739,17 @@ void ChunkManager::verify() { + void ChunkManager::locked_verify() { + locked_verify_free_chunks_count(); + locked_verify_free_chunks_total(); ++ for (ChunkIndex i = ZeroIndex; i < NumberOfFreeLists; i = next_chunk_index(i)) { ++ ChunkList* list = free_chunks(i); ++ if (list != NULL) { ++ Metachunk* chunk = list->head(); ++ while (chunk) { ++ DEBUG_ONLY(do_verify_chunk(chunk);) ++ assert(chunk->is_tagged_free(), "Chunk should be tagged as free."); ++ chunk = chunk->next(); ++ } ++ } ++ } + } + + void ChunkManager::locked_print_free_chunks(outputStream* st) { +@@ -1879,18 +2809,166 @@ ChunkList* ChunkManager::find_free_chunks_list(size_t word_size) { + return free_chunks(index); + } + ++// Helper for chunk splitting: given a target chunk size and a larger free chunk, ++// split up the larger chunk into n smaller chunks, at least one of which should be ++// the target chunk of target chunk size. The smaller chunks, including the target ++// chunk, are returned to the freelist. The pointer to the target chunk is returned. ++// Note that this chunk is supposed to be removed from the freelist right away. ++Metachunk* ChunkManager::split_chunk(size_t target_chunk_word_size, Metachunk* larger_chunk) { ++ assert(larger_chunk->word_size() > target_chunk_word_size, "Sanity"); ++ ++ const ChunkIndex larger_chunk_index = larger_chunk->get_chunk_type(); ++ const ChunkIndex target_chunk_index = get_chunk_type_by_size(target_chunk_word_size, is_class()); ++ ++ MetaWord* const region_start = (MetaWord*)larger_chunk; ++ const size_t region_word_len = larger_chunk->word_size(); ++ MetaWord* const region_end = region_start + region_word_len; ++ VirtualSpaceNode* const vsn = larger_chunk->container(); ++ OccupancyMap* const ocmap = vsn->occupancy_map(); ++ ++ // Any larger non-humongous chunk size is a multiple of any smaller chunk size. ++ // Since non-humongous chunks are aligned to their chunk size, the larger chunk should start ++ // at an address suitable to place the smaller target chunk. ++ assert_is_ptr_aligned(region_start, target_chunk_word_size); ++ ++ // Remove old chunk. ++ free_chunks(larger_chunk_index)->remove_chunk(larger_chunk); ++ larger_chunk->remove_sentinel(); ++ ++ // Prevent access to the old chunk from here on. ++ larger_chunk = NULL; ++ // ... and wipe it. ++ DEBUG_ONLY(memset(region_start, 0xfe, region_word_len * BytesPerWord)); ++ ++ // In its place create first the target chunk... ++ MetaWord* p = region_start; ++ Metachunk* target_chunk = ::new (p) Metachunk(target_chunk_index, is_class(), target_chunk_word_size, vsn); ++ assert(target_chunk == (Metachunk*)p, "Sanity"); ++ target_chunk->set_origin(origin_split); ++ ++ // Note: we do not need to mark its start in the occupancy map ++ // because it coincides with the old chunk start. ++ ++ // Mark chunk as free and return to the freelist. ++ do_update_in_use_info_for_chunk(target_chunk, false); ++ free_chunks(target_chunk_index)->return_chunk_at_head(target_chunk); ++ ++ // This chunk should now be valid and can be verified. ++ DEBUG_ONLY(do_verify_chunk(target_chunk)); ++ ++ // In the remaining space create the remainder chunks. ++ p += target_chunk->word_size(); ++ assert(p < region_end, "Sanity"); ++ ++ while (p < region_end) { ++ ++ // Find the largest chunk size which fits the alignment requirements at address p. ++ ChunkIndex this_chunk_index = prev_chunk_index(larger_chunk_index); ++ size_t this_chunk_word_size = 0; ++ for(;;) { ++ this_chunk_word_size = get_size_for_nonhumongous_chunktype(this_chunk_index, is_class()); ++ if (is_aligned(p, this_chunk_word_size * BytesPerWord)) { ++ break; ++ } else { ++ this_chunk_index = prev_chunk_index(this_chunk_index); ++ assert(this_chunk_index >= target_chunk_index, "Sanity"); ++ } ++ } ++ ++ assert(this_chunk_word_size >= target_chunk_word_size, "Sanity"); ++ assert(is_aligned(p, this_chunk_word_size * BytesPerWord), "Sanity"); ++ assert(p + this_chunk_word_size <= region_end, "Sanity"); ++ ++ // Create splitting chunk. ++ Metachunk* this_chunk = ::new (p) Metachunk(this_chunk_index, is_class(), this_chunk_word_size, vsn); ++ assert(this_chunk == (Metachunk*)p, "Sanity"); ++ this_chunk->set_origin(origin_split); ++ ocmap->set_chunk_starts_at_address(p, true); ++ do_update_in_use_info_for_chunk(this_chunk, false); ++ ++ // This chunk should be valid and can be verified. ++ DEBUG_ONLY(do_verify_chunk(this_chunk)); ++ ++ // Return this chunk to freelist and correct counter. ++ free_chunks(this_chunk_index)->return_chunk_at_head(this_chunk); ++ _free_chunks_count ++; ++ ++ if (TraceMetadataChunkAllocation) { ++ gclog_or_tty->print_cr("Created chunk at " PTR_FORMAT ", word size " ++ SIZE_FORMAT_HEX " (%s), in split region [" PTR_FORMAT "..." PTR_FORMAT ").", ++ p2i(this_chunk), this_chunk->word_size(), chunk_size_name(this_chunk_index), ++ p2i(region_start), p2i(region_end)); ++ } ++ ++ p += this_chunk_word_size; ++ ++ } ++ ++ return target_chunk; ++} ++ + Metachunk* ChunkManager::free_chunks_get(size_t word_size) { + assert_lock_strong(SpaceManager::expand_lock()); + + slow_locked_verify(); + + Metachunk* chunk = NULL; ++ bool we_did_split_a_chunk = false; ++ + if (list_index(word_size) != HumongousIndex) { ++ + ChunkList* free_list = find_free_chunks_list(word_size); + assert(free_list != NULL, "Sanity check"); + + chunk = free_list->head(); + ++ if (chunk == NULL) { ++ // Split large chunks into smaller chunks if there are no smaller chunks, just large chunks. ++ // This is the counterpart of the coalescing-upon-chunk-return. ++ ++ ChunkIndex target_chunk_index = get_chunk_type_by_size(word_size, is_class()); ++ ++ // Is there a larger chunk we could split? ++ Metachunk* larger_chunk = NULL; ++ ChunkIndex larger_chunk_index = next_chunk_index(target_chunk_index); ++ while (larger_chunk == NULL && larger_chunk_index < NumberOfFreeLists) { ++ larger_chunk = free_chunks(larger_chunk_index)->head(); ++ if (larger_chunk == NULL) { ++ larger_chunk_index = next_chunk_index(larger_chunk_index); ++ } ++ } ++ ++ if (larger_chunk != NULL) { ++ assert(larger_chunk->word_size() > word_size, "Sanity"); ++ assert(larger_chunk->get_chunk_type() == larger_chunk_index, "Sanity"); ++ ++ // We found a larger chunk. Lets split it up: ++ // - remove old chunk ++ // - in its place, create new smaller chunks, with at least one chunk ++ // being of target size, the others sized as large as possible. This ++ // is to make sure the resulting chunks are "as coalesced as possible" ++ // (similar to VirtualSpaceNode::retire()). ++ // Note: during this operation both ChunkManager and VirtualSpaceNode ++ // are temporarily invalid, so be careful with asserts. ++ if (TraceMetadataChunkAllocation) { ++ gclog_or_tty->print_cr("%s: splitting chunk " PTR_FORMAT ++ ", word size " SIZE_FORMAT_HEX " (%s), to get a chunk of word size " SIZE_FORMAT_HEX " (%s)...", ++ (is_class() ? "class space" : "metaspace"), p2i(larger_chunk), larger_chunk->word_size(), ++ chunk_size_name(larger_chunk_index), word_size, chunk_size_name(target_chunk_index)); ++ } ++ ++ chunk = split_chunk(word_size, larger_chunk); ++ ++ // This should have worked. ++ assert(chunk != NULL, "Sanity"); ++ assert(chunk->word_size() == word_size, "Sanity"); ++ assert(chunk->is_tagged_free(), "Sanity"); ++ ++ we_did_split_a_chunk = true; ++ ++ } ++ } ++ + if (chunk == NULL) { + return NULL; + } +@@ -1899,9 +2977,8 @@ Metachunk* ChunkManager::free_chunks_get(size_t word_size) { + free_list->remove_chunk(chunk); + + if (TraceMetadataChunkAllocation && Verbose) { +- gclog_or_tty->print_cr("ChunkManager::free_chunks_get: free_list " +- PTR_FORMAT " head " PTR_FORMAT " size " SIZE_FORMAT, +- free_list, chunk, chunk->word_size()); ++ gclog_or_tty->print_cr("ChunkManager::free_chunks_get: free_list: " PTR_FORMAT " chunks left: " SSIZE_FORMAT ".", ++ p2i(free_list), free_list->count()); + } + } else { + chunk = humongous_dictionary()->get_chunk( +@@ -1921,20 +2998,26 @@ Metachunk* ChunkManager::free_chunks_get(size_t word_size) { + } + } + +- // Chunk is being removed from the chunks free list. +- dec_free_chunks_total(chunk->word_size()); ++ // Chunk has been removed from the chunk manager; update counters. ++ account_for_removed_chunk(chunk); ++ do_update_in_use_info_for_chunk(chunk, true); ++ chunk->container()->inc_container_count(); ++ chunk->inc_use_count(); + + // Remove it from the links to this freelist + chunk->set_next(NULL); + chunk->set_prev(NULL); ++ ++ // Run some verifications (some more if we did a chunk split) + #ifdef ASSERT +- // Chunk is no longer on any freelist. Setting to false make container_count_slow() +- // work. +- chunk->set_is_tagged_free(false); ++ locked_verify(); ++ VirtualSpaceNode* const vsn = chunk->container(); ++ vsn->verify(); ++ if (we_did_split_a_chunk) { ++ vsn->verify_free_chunks_are_ideally_merged(); ++ } + #endif +- chunk->container()->inc_container_count(); + +- slow_locked_verify(); + return chunk; + } + +@@ -1968,6 +3051,97 @@ Metachunk* ChunkManager::chunk_freelist_allocate(size_t word_size) { + return chunk; + } + ++void ChunkManager::return_single_chunk(ChunkIndex index, Metachunk* chunk) { ++ assert_lock_strong(SpaceManager::expand_lock()); ++ DEBUG_ONLY(do_verify_chunk(chunk);) ++ assert(chunk->get_chunk_type() == index, "Chunk does not match expected index."); ++ assert(chunk != NULL, "Expected chunk."); ++ assert(chunk->container() != NULL, "Container should have been set."); ++ assert(chunk->is_tagged_free() == false, "Chunk should be in use."); ++ index_bounds_check(index); ++ ++ // Note: mangle *before* returning the chunk to the freelist or dictionary. It does not ++ // matter for the freelist (non-humongous chunks), but the humongous chunk dictionary ++ // keeps tree node pointers in the chunk payload area which mangle will overwrite. ++ DEBUG_ONLY(chunk->mangle(badMetaWordVal);) ++ ++ if (index != HumongousIndex) { ++ // Return non-humongous chunk to freelist. ++ ChunkList* list = free_chunks(index); ++ assert(list->size() == chunk->word_size(), "Wrong chunk type."); ++ list->return_chunk_at_head(chunk); ++ if (TraceMetadataChunkAllocation) { ++ gclog_or_tty->print_cr("returned one %s chunk at " PTR_FORMAT " to freelist.", ++ chunk_size_name(index), p2i(chunk)); ++ } ++ } else { ++ // Return humongous chunk to dictionary. ++ assert(chunk->word_size() > free_chunks(MediumIndex)->size(), "Wrong chunk type."); ++ assert(chunk->word_size() % free_chunks(SpecializedIndex)->size() == 0, ++ "Humongous chunk has wrong alignment."); ++ _humongous_dictionary.return_chunk(chunk); ++ if (TraceMetadataChunkAllocation) { ++ gclog_or_tty->print_cr("returned one %s chunk at " PTR_FORMAT " (word size " SIZE_FORMAT ") to freelist.", ++ chunk_size_name(index), p2i(chunk), chunk->word_size()); ++ } ++ } ++ chunk->container()->dec_container_count(); ++ do_update_in_use_info_for_chunk(chunk, false); ++ ++ // Chunk has been added; update counters. ++ account_for_added_chunk(chunk); ++ ++ // Attempt coalesce returned chunks with its neighboring chunks: ++ // if this chunk is small or special, attempt to coalesce to a medium chunk. ++ if (index == SmallIndex || index == SpecializedIndex) { ++ if (!attempt_to_coalesce_around_chunk(chunk, MediumIndex)) { ++ // This did not work. But if this chunk is special, we still may form a small chunk? ++ if (index == SpecializedIndex) { ++ if (!attempt_to_coalesce_around_chunk(chunk, SmallIndex)) { ++ // give up. ++ } ++ } ++ } ++ } ++ ++} ++ ++void ChunkManager::return_chunk_list(ChunkIndex index, Metachunk* chunks) { ++ index_bounds_check(index); ++ if (chunks == NULL) { ++ return; ++ } ++ // tracing ++ if (TraceMetadataChunkAllocation) { ++ gclog_or_tty->print_cr("returning list of %s chunks...", chunk_size_name(index)); ++ } ++ ++ unsigned num_chunks_returned = 0; ++ size_t size_chunks_returned = 0; ++ Metachunk* cur = chunks; ++ while (cur != NULL) { ++ // Capture the next link before it is changed ++ // by the call to return_chunk_at_head(); ++ Metachunk* next = cur->next(); ++ // tracing ++ num_chunks_returned ++; ++ size_chunks_returned += cur->word_size(); ++ ++ return_single_chunk(index, cur); ++ cur = next; ++ } ++ // tracing ++ if (TraceMetadataChunkAllocation) { ++ gclog_or_tty->print_cr("returned %u %s chunks to freelist, total word size " SIZE_FORMAT ".", ++ num_chunks_returned, chunk_size_name(index), size_chunks_returned); ++ if (index != HumongousIndex) { ++ gclog_or_tty->print_cr("updated freelist count: " SIZE_FORMAT ".", free_chunks(index)->size()); ++ } else { ++ gclog_or_tty->print_cr("updated dictionary count " SIZE_FORMAT ".", _humongous_dictionary.total_count()); ++ } ++ } ++} ++ + void ChunkManager::print_on(outputStream* out) const { + if (PrintFLSStatistics != 0) { + _humongous_dictionary.report_statistics(); +@@ -2394,31 +3568,6 @@ void SpaceManager::initialize() { + } + } + +-void ChunkManager::return_chunks(ChunkIndex index, Metachunk* chunks) { +- if (chunks == NULL) { +- return; +- } +- ChunkList* list = free_chunks(index); +- assert(list->size() == chunks->word_size(), "Mismatch in chunk sizes"); +- assert_lock_strong(SpaceManager::expand_lock()); +- Metachunk* cur = chunks; +- +- // This returns chunks one at a time. If a new +- // class List can be created that is a base class +- // of FreeList then something like FreeList::prepend() +- // can be used in place of this loop +- while (cur != NULL) { +- assert(cur->container() != NULL, "Container should have been set"); +- cur->container()->dec_container_count(); +- // Capture the next link before it is changed +- // by the call to return_chunk_at_head(); +- Metachunk* next = cur->next(); +- DEBUG_ONLY(cur->set_is_tagged_free(true);) +- list->return_chunk_at_head(cur); +- cur = next; +- } +-} +- + SpaceManager::~SpaceManager() { + // This call this->_lock which can't be done while holding expand_lock() + assert(sum_capacity_in_chunks_in_use() == allocated_chunks_words(), +@@ -2429,6 +3578,11 @@ SpaceManager::~SpaceManager() { + MutexLockerEx fcl(SpaceManager::expand_lock(), + Mutex::_no_safepoint_check_flag); + ++ assert(sum_count_in_chunks_in_use() == allocated_chunks_count(), ++ err_msg("sum_count_in_chunks_in_use() " SIZE_FORMAT ++ " allocated_chunks_count() " SIZE_FORMAT, ++ sum_count_in_chunks_in_use(), allocated_chunks_count())); ++ + chunk_manager()->slow_locked_verify(); + + dec_total_from_size_metrics(); +@@ -2436,112 +3590,27 @@ SpaceManager::~SpaceManager() { + if (TraceMetadataChunkAllocation && Verbose) { + gclog_or_tty->print_cr("~SpaceManager(): " PTR_FORMAT, this); + locked_print_chunks_in_use_on(gclog_or_tty); ++ if (block_freelists() != NULL) { ++ block_freelists()->print_on(gclog_or_tty); ++ } + } + +- // Do not mangle freed Metachunks. The chunk size inside Metachunks +- // is during the freeing of a VirtualSpaceNodes. +- +- // Have to update before the chunks_in_use lists are emptied +- // below. +- chunk_manager()->inc_free_chunks_total(allocated_chunks_words(), +- sum_count_in_chunks_in_use()); +- + // Add all the chunks in use by this space manager + // to the global list of free chunks. + + // Follow each list of chunks-in-use and add them to the + // free lists. Each list is NULL terminated. + +- for (ChunkIndex i = ZeroIndex; i < HumongousIndex; i = next_chunk_index(i)) { +- if (TraceMetadataChunkAllocation && Verbose) { +- gclog_or_tty->print_cr("returned %d %s chunks to freelist", +- sum_count_in_chunks_in_use(i), +- chunk_size_name(i)); +- } ++ for (ChunkIndex i = ZeroIndex; i <= HumongousIndex; i = next_chunk_index(i)) { + Metachunk* chunks = chunks_in_use(i); +- chunk_manager()->return_chunks(i, chunks); ++ chunk_manager()->return_chunk_list(i, chunks); + set_chunks_in_use(i, NULL); +- if (TraceMetadataChunkAllocation && Verbose) { +- gclog_or_tty->print_cr("updated freelist count %d %s", +- chunk_manager()->free_chunks(i)->count(), +- chunk_size_name(i)); +- } +- assert(i != HumongousIndex, "Humongous chunks are handled explicitly later"); + } + +- // The medium chunk case may be optimized by passing the head and +- // tail of the medium chunk list to add_at_head(). The tail is often +- // the current chunk but there are probably exceptions. +- +- // Humongous chunks +- if (TraceMetadataChunkAllocation && Verbose) { +- gclog_or_tty->print_cr("returned %d %s humongous chunks to dictionary", +- sum_count_in_chunks_in_use(HumongousIndex), +- chunk_size_name(HumongousIndex)); +- gclog_or_tty->print("Humongous chunk dictionary: "); +- } +- // Humongous chunks are never the current chunk. +- Metachunk* humongous_chunks = chunks_in_use(HumongousIndex); + +- while (humongous_chunks != NULL) { +-#ifdef ASSERT +- humongous_chunks->set_is_tagged_free(true); +-#endif +- if (TraceMetadataChunkAllocation && Verbose) { +- gclog_or_tty->print(PTR_FORMAT " (" SIZE_FORMAT ") ", +- humongous_chunks, +- humongous_chunks->word_size()); +- } +- assert(humongous_chunks->word_size() == (size_t) +- align_size_up(humongous_chunks->word_size(), +- smallest_chunk_size()), +- err_msg("Humongous chunk size is wrong: word size " SIZE_FORMAT +- " granularity %d", +- humongous_chunks->word_size(), smallest_chunk_size())); +- Metachunk* next_humongous_chunks = humongous_chunks->next(); +- humongous_chunks->container()->dec_container_count(); +- chunk_manager()->humongous_dictionary()->return_chunk(humongous_chunks); +- humongous_chunks = next_humongous_chunks; +- } +- if (TraceMetadataChunkAllocation && Verbose) { +- gclog_or_tty->cr(); +- gclog_or_tty->print_cr("updated dictionary count %d %s", +- chunk_manager()->humongous_dictionary()->total_count(), +- chunk_size_name(HumongousIndex)); +- } + chunk_manager()->slow_locked_verify(); + } + +-const char* SpaceManager::chunk_size_name(ChunkIndex index) const { +- switch (index) { +- case SpecializedIndex: +- return "Specialized"; +- case SmallIndex: +- return "Small"; +- case MediumIndex: +- return "Medium"; +- case HumongousIndex: +- return "Humongous"; +- default: +- return NULL; +- } +-} +- +-ChunkIndex ChunkManager::list_index(size_t size) { +- if (free_chunks(SpecializedIndex)->size() == size) { +- return SpecializedIndex; +- } +- if (free_chunks(SmallIndex)->size() == size) { +- return SmallIndex; +- } +- if (free_chunks(MediumIndex)->size() == size) { +- return MediumIndex; +- } +- +- assert(size > free_chunks(MediumIndex)->size(), "Not a humongous chunk"); +- return HumongousIndex; +-} +- + void SpaceManager::deallocate(MetaWord* p, size_t word_size) { + assert_lock_strong(_lock); + size_t raw_word_size = get_raw_word_size(word_size); +@@ -2693,8 +3762,8 @@ void SpaceManager::verify() { + for (ChunkIndex i = ZeroIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) { + Metachunk* curr = chunks_in_use(i); + while (curr != NULL) { +- curr->verify(); +- verify_chunk_size(curr); ++ DEBUG_ONLY(do_verify_chunk(curr);) ++ assert(curr->is_tagged_free() == false, "Chunk should be tagged as in use."); + curr = curr->next(); + } + } +@@ -2767,7 +3836,7 @@ void SpaceManager::mangle_freed_chunks() { + for (Metachunk* curr = chunks_in_use(index); + curr != NULL; + curr = curr->next()) { +- curr->mangle(); ++ curr->mangle(uninitMetaWordVal); + } + } + } +@@ -2988,9 +4057,9 @@ void MetaspaceAux::print_on(outputStream* out, Metaspace::MetadataType mdtype) { + size_t free_bytes = free_bytes_slow(mdtype); + size_t used_and_free = used_bytes + free_bytes + + free_chunks_capacity_bytes; +- out->print_cr(" Chunk accounting: used in chunks " SIZE_FORMAT ++ out->print_cr(" Chunk accounting: (used in chunks " SIZE_FORMAT + "K + unused in chunks " SIZE_FORMAT "K + " +- " capacity in free chunks " SIZE_FORMAT "K = " SIZE_FORMAT ++ " capacity in free chunks " SIZE_FORMAT "K) = " SIZE_FORMAT + "K capacity in allocated chunks " SIZE_FORMAT "K", + used_bytes / K, + free_bytes / K, +@@ -3255,6 +4324,31 @@ void MetaspaceAux::dump(outputStream* out) { + print_waste(out); + } + ++// Prints an ASCII representation of the given space. ++void MetaspaceAux::print_metaspace_map(outputStream* out, Metaspace::MetadataType mdtype) { ++ MutexLockerEx cl(SpaceManager::expand_lock(), Mutex::_no_safepoint_check_flag); ++ const bool for_class = mdtype == Metaspace::ClassType ? true : false; ++ VirtualSpaceList* const vsl = for_class ? Metaspace::class_space_list() : Metaspace::space_list(); ++ if (vsl != NULL) { ++ if (for_class) { ++ if (!Metaspace::using_class_space()) { ++ out->print_cr("No Class Space."); ++ return; ++ } ++ out->print_raw("---- Metaspace Map (Class Space) ----"); ++ } else { ++ out->print_raw("---- Metaspace Map (Non-Class Space) ----"); ++ } ++ // Print legend: ++ out->cr(); ++ out->print_cr("Chunk Types (uppercase chunks are in use): x-specialized, s-small, m-medium, h-humongous."); ++ out->cr(); ++ VirtualSpaceList* const vsl = for_class ? Metaspace::class_space_list() : Metaspace::space_list(); ++ vsl->print_map(out); ++ out->cr(); ++ } ++} ++ + void MetaspaceAux::verify_free_chunks() { + Metaspace::chunk_manager_metadata()->verify(); + if (Metaspace::using_class_space()) { +@@ -3523,7 +4617,7 @@ void Metaspace::initialize_class_space(ReservedSpace rs) { + err_msg(SIZE_FORMAT " != " UINTX_FORMAT, rs.size(), CompressedClassSpaceSize)); + assert(using_class_space(), "Must be using class space"); + _class_space_list = new VirtualSpaceList(rs); +- _chunk_manager_class = new ChunkManager(ClassSpecializedChunk, ClassSmallChunk, ClassMediumChunk); ++ _chunk_manager_class = new ChunkManager(true/*is_class*/); + + if (!_class_space_list->initialization_succeeded()) { + vm_exit_during_initialization("Failed to setup compressed class space virtual space list."); +@@ -3627,7 +4721,7 @@ void Metaspace::global_initialize() { + cds_total = FileMapInfo::shared_spaces_size(); + cds_total = align_size_up(cds_total, _reserve_alignment); + _space_list = new VirtualSpaceList(cds_total/wordSize); +- _chunk_manager_metadata = new ChunkManager(SpecializedChunk, SmallChunk, MediumChunk); ++ _chunk_manager_metadata = new ChunkManager(false/*metaspace*/); + + if (!_space_list->initialization_succeeded()) { + vm_exit_during_initialization("Unable to dump shared archive.", NULL); +@@ -3777,7 +4871,7 @@ void Metaspace::global_initialize() { + + // Initialize the list of virtual spaces. + _space_list = new VirtualSpaceList(word_size); +- _chunk_manager_metadata = new ChunkManager(SpecializedChunk, SmallChunk, MediumChunk); ++ _chunk_manager_metadata = new ChunkManager(false/*metaspace*/); + + if (!_space_list->initialization_succeeded()) { + vm_exit_during_initialization("Unable to setup metadata virtual space list.", NULL); +@@ -4071,6 +5165,8 @@ void Metaspace::report_metadata_oome(ClassLoaderData* loader_data, size_t word_s + ChunkManager::print_all_chunkmanagers(gclog_or_tty); + } + ++ MetaspaceAux::print_metaspace_map(gclog_or_tty, mdtype); ++ + bool out_of_compressed_class_space = false; + if (is_class_space_allocation(mdtype)) { + Metaspace* metaspace = loader_data->metaspace_non_null(); +@@ -4236,6 +5332,24 @@ void Metaspace::dump(outputStream* const out) const { + } + } + ++#ifdef ASSERT ++static void do_verify_chunk(Metachunk* chunk) { ++ guarantee(chunk != NULL, "Sanity"); ++ // Verify chunk itself; then verify that it is consistent with the ++ // occupany map of its containing node. ++ chunk->verify(); ++ VirtualSpaceNode* const vsn = chunk->container(); ++ OccupancyMap* const ocmap = vsn->occupancy_map(); ++ ocmap->verify_for_chunk(chunk); ++} ++#endif ++ ++static void do_update_in_use_info_for_chunk(Metachunk* chunk, bool inuse) { ++ chunk->set_is_tagged_free(!inuse); ++ OccupancyMap* const ocmap = chunk->container()->occupancy_map(); ++ ocmap->set_region_in_use((MetaWord*)chunk, chunk->word_size(), inuse); ++} ++ + /////////////// Unit tests /////////////// + + #ifndef PRODUCT +@@ -4326,16 +5440,16 @@ class TestVirtualSpaceNodeTest { + STATIC_ASSERT(SmallChunk % SpecializedChunk == 0); + + { // No committed memory in VSN +- ChunkManager cm(SpecializedChunk, SmallChunk, MediumChunk); +- VirtualSpaceNode vsn(vsn_test_size_bytes); ++ ChunkManager cm(false); ++ VirtualSpaceNode vsn(false, vsn_test_size_bytes); + vsn.initialize(); + vsn.retire(&cm); + assert(cm.sum_free_chunks_count() == 0, "did not commit any memory in the VSN"); + } + + { // All of VSN is committed, half is used by chunks +- ChunkManager cm(SpecializedChunk, SmallChunk, MediumChunk); +- VirtualSpaceNode vsn(vsn_test_size_bytes); ++ ChunkManager cm(false); ++ VirtualSpaceNode vsn(false, vsn_test_size_bytes); + vsn.initialize(); + vsn.expand_by(vsn_test_size_words, vsn_test_size_words); + vsn.get_chunk_vs(MediumChunk); +@@ -4349,8 +5463,8 @@ class TestVirtualSpaceNodeTest { + // This doesn't work for systems with vm_page_size >= 16K. + if (page_chunks < MediumChunk) { + // 4 pages of VSN is committed, some is used by chunks +- ChunkManager cm(SpecializedChunk, SmallChunk, MediumChunk); +- VirtualSpaceNode vsn(vsn_test_size_bytes); ++ ChunkManager cm(false); ++ VirtualSpaceNode vsn(false, vsn_test_size_bytes); + + vsn.initialize(); + vsn.expand_by(page_chunks, page_chunks); +@@ -4370,8 +5484,8 @@ class TestVirtualSpaceNodeTest { + } + + { // Half of VSN is committed, a humongous chunk is used +- ChunkManager cm(SpecializedChunk, SmallChunk, MediumChunk); +- VirtualSpaceNode vsn(vsn_test_size_bytes); ++ ChunkManager cm(false); ++ VirtualSpaceNode vsn(false, vsn_test_size_bytes); + vsn.initialize(); + vsn.expand_by(MediumChunk * 2, MediumChunk * 2); + vsn.get_chunk_vs(MediumChunk + SpecializedChunk); // Humongous chunks will be aligned up to MediumChunk + SpecializedChunk +@@ -4402,7 +5516,7 @@ class TestVirtualSpaceNodeTest { + + static void test_is_available_positive() { + // Reserve some memory. +- VirtualSpaceNode vsn(os::vm_allocation_granularity()); ++ VirtualSpaceNode vsn(false, os::vm_allocation_granularity()); + assert(vsn.initialize(), "Failed to setup VirtualSpaceNode"); + + // Commit some memory. +@@ -4420,7 +5534,7 @@ class TestVirtualSpaceNodeTest { + + static void test_is_available_negative() { + // Reserve some memory. +- VirtualSpaceNode vsn(os::vm_allocation_granularity()); ++ VirtualSpaceNode vsn(false, os::vm_allocation_granularity()); + assert(vsn.initialize(), "Failed to setup VirtualSpaceNode"); + + // Commit some memory. +@@ -4435,7 +5549,7 @@ class TestVirtualSpaceNodeTest { + + static void test_is_available_overflow() { + // Reserve some memory. +- VirtualSpaceNode vsn(os::vm_allocation_granularity()); ++ VirtualSpaceNode vsn(false, os::vm_allocation_granularity()); + assert(vsn.initialize(), "Failed to setup VirtualSpaceNode"); + + // Commit some memory. +@@ -4460,11 +5574,240 @@ class TestVirtualSpaceNodeTest { + } + }; + +-void TestVirtualSpaceNode_test() { +- TestVirtualSpaceNodeTest::test(); +- TestVirtualSpaceNodeTest::test_is_available(); ++#ifdef DEBUG_VERBOSE ++ ++struct chunkmanager_statistics_t { ++ int num_specialized_chunks; ++ int num_small_chunks; ++ int num_medium_chunks; ++ int num_humongous_chunks; ++}; ++ ++void test_metaspace_retrieve_chunkmanager_statistics(Metaspace::MetadataType mdType, chunkmanager_statistics_t* out) { ++ ChunkManager* const chunk_manager = Metaspace::get_chunk_manager(mdType); ++ ChunkManager::ChunkManagerStatistics stat; ++ chunk_manager->get_statistics(&stat); ++ out->num_specialized_chunks = (int)stat.num_by_type[SpecializedIndex]; ++ out->num_small_chunks = (int)stat.num_by_type[SmallIndex]; ++ out->num_medium_chunks = (int)stat.num_by_type[MediumIndex]; ++ out->num_humongous_chunks = (int)stat.num_humongous_chunks; ++} ++ ++static void print_chunkmanager_statistics(outputStream* st, Metaspace::MetadataType mdType) { ++ chunkmanager_statistics_t stat; ++ test_metaspace_retrieve_chunkmanager_statistics(mdType, &stat); ++ st->print_cr("free chunks: %d / %d / %d / %d", stat.num_specialized_chunks, stat.num_small_chunks, ++ stat.num_medium_chunks, stat.num_humongous_chunks); ++} ++ ++#endif // DEBUG_VERBOSE ++ ++struct chunk_geometry_t { ++ size_t specialized_chunk_word_size; ++ size_t small_chunk_word_size; ++ size_t medium_chunk_word_size; ++}; ++ ++void test_metaspace_retrieve_chunk_geometry(Metaspace::MetadataType mdType, chunk_geometry_t* out) { ++ if (mdType == Metaspace::NonClassType) { ++ out->specialized_chunk_word_size = SpecializedChunk; ++ out->small_chunk_word_size = SmallChunk; ++ out->medium_chunk_word_size = MediumChunk; ++ } else { ++ out->specialized_chunk_word_size = ClassSpecializedChunk; ++ out->small_chunk_word_size = ClassSmallChunk; ++ out->medium_chunk_word_size = ClassMediumChunk; ++ } ++} ++ ++#define NUM_PARALLEL_METASPACES 50 ++#define MAX_PER_METASPACE_ALLOCATION_WORDSIZE (512 * K) ++ ++class MetaspaceAllocationTest { ++protected: ++ ++ struct { ++ size_t allocated; ++ Mutex* lock; ++ Metaspace* space; ++ bool is_empty() const { return allocated == 0; } ++ bool is_full() const { return allocated >= MAX_PER_METASPACE_ALLOCATION_WORDSIZE; } ++ } _spaces[NUM_PARALLEL_METASPACES]; ++ ++ chunk_geometry_t _chunk_geometry; ++ ++ void create_space(int i) { ++ assert(i >= 0 && i < NUM_PARALLEL_METASPACES, "Sanity"); ++ assert(_spaces[i].space == NULL && _spaces[i].allocated == 0, "Sanity"); ++ if (_spaces[i].lock == NULL) { ++ _spaces[i].lock = new Mutex(Monitor::native, "gtest-MetaspaceAllocationTest-lock", false); ++ assert(_spaces[i].lock != NULL, "_spaces[i].lock is NULL"); ++ } ++ // Let every ~10th space be an anonymous one to test different allocation patterns. ++ const Metaspace::MetaspaceType msType = (os::random() % 100 < 10) ? ++ Metaspace::AnonymousMetaspaceType : Metaspace::StandardMetaspaceType; ++ _spaces[i].space = new Metaspace(_spaces[i].lock, msType); ++ _spaces[i].allocated = 0; ++ assert(_spaces[i].space != NULL, "_spaces[i].space is NULL"); ++ } ++ ++ // Returns the index of a random space where index is [0..metaspaces) and which is ++ // empty, non-empty or full. ++ // Returns -1 if no matching space exists. ++ enum fillgrade { fg_empty, fg_non_empty, fg_full }; ++ int get_random_matching_space(int metaspaces, fillgrade fg) { ++ const int start_index = os::random() % metaspaces; ++ int i = start_index; ++ do { ++ if (fg == fg_empty && _spaces[i].is_empty()) { ++ return i; ++ } else if ((fg == fg_full && _spaces[i].is_full()) || ++ (fg == fg_non_empty && !_spaces[i].is_full() && !_spaces[i].is_empty())) { ++ return i; ++ } ++ i ++; ++ if (i == metaspaces) { ++ i = 0; ++ } ++ } while (i != start_index); ++ return -1; ++ } ++ ++ int get_random_emtpy_space(int metaspaces) { return get_random_matching_space(metaspaces, fg_empty); } ++ int get_random_non_emtpy_space(int metaspaces) { return get_random_matching_space(metaspaces, fg_non_empty); } ++ int get_random_full_space(int metaspaces) { return get_random_matching_space(metaspaces, fg_full); } ++ ++public: ++ ++ virtual void SetUp() { ++ ::memset(_spaces, 0, sizeof(_spaces)); ++ test_metaspace_retrieve_chunk_geometry(Metaspace::NonClassType, &_chunk_geometry); ++ } ++ ++ virtual void TearDown() { ++ for (int i = 0; i < NUM_PARALLEL_METASPACES; i ++) { ++ if (_spaces[i].space != NULL) { ++ delete _spaces[i].space; ++ delete _spaces[i].lock; ++ } ++ } ++ } ++ ++ void do_test(Metaspace::MetadataType mdType, int metaspaces, int phases, int allocs_per_phase, ++ float probability_for_large_allocations // 0.0-1.0 ++ ) { ++ // Alternate between breathing in (allocating n blocks for a random Metaspace) and ++ // breathing out (deleting a random Metaspace). The intent is to stress the coalescation ++ // and splitting of free chunks. ++ int phases_done = 0; ++ bool allocating = true; ++ while (phases_done < phases) { ++ bool force_switch = false; ++ if (allocating) { ++ // Allocate space from metaspace, with a preference for completely empty spaces. This ++ // should provide a good mixture of metaspaces in the virtual space. ++ int index = get_random_emtpy_space(metaspaces); ++ if (index == -1) { ++ index = get_random_non_emtpy_space(metaspaces); ++ } ++ if (index == -1) { ++ // All spaces are full, switch to freeing. ++ force_switch = true; ++ } else { ++ // create space if it does not yet exist. ++ if (_spaces[index].space == NULL) { ++ create_space(index); ++ } ++ // Allocate a bunch of blocks from it. Mostly small stuff but mix in large allocations ++ // to force humongous chunk allocations. ++ int allocs_done = 0; ++ while (allocs_done < allocs_per_phase && !_spaces[index].is_full()) { ++ size_t size = 0; ++ int r = os::random() % 1000; ++ if ((float)r < probability_for_large_allocations * 1000.0) { ++ size = (os::random() % _chunk_geometry.medium_chunk_word_size) + _chunk_geometry.medium_chunk_word_size; ++ } else { ++ size = os::random() % 64; ++ } ++ MetaWord* const p = _spaces[index].space->allocate(size, mdType); ++ if (p == NULL) { ++ // We very probably did hit the metaspace "until-gc" limit. ++#ifdef DEBUG_VERBOSE ++ tty->print_cr("OOM for " SIZE_FORMAT " words. ", size); ++#endif ++ // Just switch to deallocation and resume tests. ++ force_switch = true; ++ break; ++ } else { ++ _spaces[index].allocated += size; ++ allocs_done ++; ++ } ++ } ++ } ++ } else { ++ // freeing: find a metaspace and delete it, with preference for completely filled spaces. ++ int index = get_random_full_space(metaspaces); ++ if (index == -1) { ++ index = get_random_non_emtpy_space(metaspaces); ++ } ++ if (index == -1) { ++ force_switch = true; ++ } else { ++ assert(_spaces[index].space != NULL && _spaces[index].allocated > 0, "Sanity"); ++ delete _spaces[index].space; ++ _spaces[index].space = NULL; ++ _spaces[index].allocated = 0; ++ } ++ } ++ ++ if (force_switch) { ++ allocating = !allocating; ++ } else { ++ // periodically switch between allocating and freeing, but prefer allocation because ++ // we want to intermingle allocations of multiple metaspaces. ++ allocating = os::random() % 5 < 4; ++ } ++ phases_done ++; ++#ifdef DEBUG_VERBOSE ++ int metaspaces_in_use = 0; ++ size_t total_allocated = 0; ++ for (int i = 0; i < metaspaces; i ++) { ++ if (_spaces[i].allocated > 0) { ++ total_allocated += _spaces[i].allocated; ++ metaspaces_in_use ++; ++ } ++ } ++ tty->print("%u:\tspaces: %d total words: " SIZE_FORMAT "\t\t\t", phases_done, metaspaces_in_use, total_allocated); ++ print_chunkmanager_statistics(tty, mdType); ++#endif ++ } ++#ifdef DEBUG_VERBOSE ++ tty->print_cr("Test finished. "); ++ MetaspaceAux::print_metaspace_map(tty, mdType); ++ print_chunkmanager_statistics(tty, mdType); ++#endif ++ } ++}; ++ ++void MetaspaceAllocation_test(Metaspace::MetadataType mdType, int metaspaces, int phases, int allocs_per_phase, float probability_for_large_allocations) { ++ MetaspaceAllocationTest test; ++ test.SetUp(); ++ test.do_test(mdType, metaspaces, phases, allocs_per_phase, probability_for_large_allocations); ++ test.TearDown(); + } + ++void run_MetaspaceAllocation_test(){ ++ MetaspaceAllocation_test(Metaspace::NonClassType, 1, 1000, 100, 0); ++ MetaspaceAllocation_test(Metaspace::ClassType, 1, 1000, 100, 0); ++ MetaspaceAllocation_test(Metaspace::NonClassType, NUM_PARALLEL_METASPACES, 100, 1000, 0.0); ++ MetaspaceAllocation_test(Metaspace::ClassType, NUM_PARALLEL_METASPACES, 100, 1000, 0.0); ++ MetaspaceAllocation_test(Metaspace::NonClassType, NUM_PARALLEL_METASPACES, 100, 1000, .006f); ++} ++ ++#endif // !PRODUCT ++ ++#ifdef ASSERT ++ + // The following test is placed here instead of a gtest / unittest file + // because the ChunkManager class is only available in this file. + class SpaceManagerTest : AllStatic { +@@ -4517,7 +5860,7 @@ void SpaceManager_test_adjust_initial_chunk_size() { + // The following test is placed here instead of a gtest / unittest file + // because the ChunkManager class is only available in this file. + void ChunkManager_test_list_index() { +- ChunkManager manager(ClassSpecializedChunk, ClassSmallChunk, ClassMediumChunk); ++ ChunkManager manager(true); + + // Test previous bug where a query for a humongous class metachunk, + // incorrectly matched the non-class medium metachunk size. +@@ -4550,4 +5893,5 @@ void ChunkManager_test_list_index() { + } + } + +-#endif ++#endif // ASSERT ++ +diff --git a/hotspot/src/share/vm/memory/metaspace.hpp b/hotspot/src/share/vm/memory/metaspace.hpp +index ff1b23299..11944d393 100644 +--- a/hotspot/src/share/vm/memory/metaspace.hpp ++++ b/hotspot/src/share/vm/memory/metaspace.hpp +@@ -147,11 +147,6 @@ class Metaspace : public CHeapObj { + return mdtype == ClassType ? class_vsm() : vsm(); + } + +- // Allocate space for metadata of type mdtype. This is space +- // within a Metachunk and is used by +- // allocate(ClassLoaderData*, size_t, bool, MetadataType, TRAPS) +- MetaWord* allocate(size_t word_size, MetadataType mdtype); +- + // Virtual Space lists for both classes and other metadata + static VirtualSpaceList* _space_list; + static VirtualSpaceList* _class_space_list; +@@ -162,6 +157,10 @@ class Metaspace : public CHeapObj { + static const MetaspaceTracer* _tracer; + + public: ++ // Allocate space for metadata of type mdtype. This is space ++ // within a Metachunk and is used by ++ // allocate(ClassLoaderData*, size_t, bool, MetadataType, TRAPS) ++ MetaWord* allocate(size_t word_size, MetadataType mdtype); + static VirtualSpaceList* space_list() { return _space_list; } + static VirtualSpaceList* class_space_list() { return _class_space_list; } + static VirtualSpaceList* get_space_list(MetadataType mdtype) { +@@ -176,6 +175,11 @@ class Metaspace : public CHeapObj { + return mdtype == ClassType ? chunk_manager_class() : chunk_manager_metadata(); + } + ++ // convenience function ++ static ChunkManager* get_chunk_manager(bool is_class) { ++ return is_class ? chunk_manager_class() : chunk_manager_metadata(); ++ } ++ + static const MetaspaceTracer* tracer() { return _tracer; } + + private: +@@ -386,6 +390,10 @@ class MetaspaceAux : AllStatic { + + static void print_class_waste(outputStream* out); + static void print_waste(outputStream* out); ++ ++ // Prints an ASCII representation of the given space. ++ static void print_metaspace_map(outputStream* out, Metaspace::MetadataType mdtype); ++ + static void dump(outputStream* out); + static void verify_free_chunks(); + // Checks that the values returned by allocated_capacity_bytes() and +diff --git a/hotspot/src/share/vm/prims/jni.cpp b/hotspot/src/share/vm/prims/jni.cpp +index dccbf1e8a..953300ebc 100644 +--- a/hotspot/src/share/vm/prims/jni.cpp ++++ b/hotspot/src/share/vm/prims/jni.cpp +@@ -5112,9 +5112,9 @@ void TestReservedSpace_test(); + void TestReserveMemorySpecial_test(); + void TestVirtualSpace_test(); + void TestMetaspaceAux_test(); ++void run_MetaspaceAllocation_test(); + void SpaceManager_test_adjust_initial_chunk_size(); + void TestMetachunk_test(); +-void TestVirtualSpaceNode_test(); + void TestNewSize_test(); + void TestKlass_test(); + void Test_linked_list(); +@@ -5137,8 +5137,8 @@ void execute_internal_vm_tests() { + run_unit_test(TestReserveMemorySpecial_test()); + run_unit_test(TestVirtualSpace_test()); + run_unit_test(TestMetaspaceAux_test()); ++ run_unit_test(run_MetaspaceAllocation_test()); + run_unit_test(TestMetachunk_test()); +- run_unit_test(TestVirtualSpaceNode_test()); + run_unit_test(GlobalDefinitions::test_globals()); + run_unit_test(GlobalDefinitions::test_proper_unit()); + run_unit_test(GCTimerAllTest::all()); +diff --git a/hotspot/src/share/vm/utilities/globalDefinitions.hpp b/hotspot/src/share/vm/utilities/globalDefinitions.hpp +index 12eea20fc..efa430663 100644 +--- a/hotspot/src/share/vm/utilities/globalDefinitions.hpp ++++ b/hotspot/src/share/vm/utilities/globalDefinitions.hpp +@@ -1079,6 +1079,7 @@ const int badHandleValue = 0xBC; // value used to zap + const int badResourceValue = 0xAB; // value used to zap resource area + const int freeBlockPad = 0xBA; // value used to pad freed blocks. + const int uninitBlockPad = 0xF1; // value used to zap newly malloc'd blocks. ++const juint uninitMetaWordVal= 0xf7f7f7f7; // value used to zap newly allocated metachunk + const intptr_t badJNIHandleVal = (intptr_t) CONST64(0xFEFEFEFEFEFEFEFE); // value used to zap jni handle area + const juint badHeapWordVal = 0xBAADBABE; // value used to zap heap after GC + const juint badMetaWordVal = 0xBAADFADE; // value used to zap metadata heap after GC +-- +2.22.0 + diff --git a/add-8226530-ZipFile-reads-wrong-entry-size-from-ZIP6.patch b/add-8226530-ZipFile-reads-wrong-entry-size-from-ZIP6.patch new file mode 100644 index 0000000..c0df133 --- /dev/null +++ b/add-8226530-ZipFile-reads-wrong-entry-size-from-ZIP6.patch @@ -0,0 +1,274 @@ +From 131462b71b86d97bd7dadf7a099d4395d9057423 Mon Sep 17 00:00:00 2001 +Date: Thu, 21 Sep 2023 15:16:36 +0800 +Subject: add 8226530-ZipFile-reads-wrong-entry-size-from-ZIP64-en + +--- + .../share/classes/java/util/zip/ZipEntry.java | 41 +++-- + .../share/classes/java/util/zip/ZipFile.java | 2 +- + .../classes/java/util/zip/ZipInputStream.java | 4 +- + .../java/util/zip/ZipFile/Zip64SizeTest.java | 147 ++++++++++++++++++ + 4 files changed, 179 insertions(+), 15 deletions(-) + create mode 100644 jdk/test/java/util/zip/ZipFile/Zip64SizeTest.java + +diff --git a/jdk/src/share/classes/java/util/zip/ZipEntry.java b/jdk/src/share/classes/java/util/zip/ZipEntry.java +index aa93bcb36..4a15428ac 100644 +--- a/jdk/src/share/classes/java/util/zip/ZipEntry.java ++++ b/jdk/src/share/classes/java/util/zip/ZipEntry.java +@@ -1,5 +1,5 @@ + /* +- * Copyright (c) 1995, 2015, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 1995, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it +@@ -440,7 +440,7 @@ class ZipEntry implements ZipConstants, Cloneable { + * @see #getExtra() + */ + public void setExtra(byte[] extra) { +- setExtra0(extra, false); ++ setExtra0(extra, false, true); + } + + /** +@@ -450,8 +450,11 @@ class ZipEntry implements ZipConstants, Cloneable { + * the extra field data bytes + * @param doZIP64 + * if true, set size and csize from ZIP64 fields if present ++ * @param isLOC ++ * true if setting the extra field for a LOC, false if for ++ * a CEN + */ +- void setExtra0(byte[] extra, boolean doZIP64) { ++ void setExtra0(byte[] extra, boolean doZIP64, boolean isLOC) { + if (extra != null) { + if (extra.length > 0xFFFF) { + throw new IllegalArgumentException("invalid extra field length"); +@@ -468,15 +471,29 @@ class ZipEntry implements ZipConstants, Cloneable { + switch (tag) { + case EXTID_ZIP64: + if (doZIP64) { +- // LOC extra zip64 entry MUST include BOTH original +- // and compressed file size fields. +- // If invalid zip64 extra fields, simply skip. Even +- // it's rare, it's possible the entry size happens to +- // be the magic value and it "accidently" has some +- // bytes in extra match the id. +- if (sz >= 16) { +- size = get64(extra, off); +- csize = get64(extra, off + 8); ++ if (isLOC) { ++ // LOC extra zip64 entry MUST include BOTH original ++ // and compressed file size fields. ++ // If invalid zip64 extra fields, simply skip. Even ++ // it's rare, it's possible the entry size happens to ++ // be the magic value and it "accidently" has some ++ // bytes in extra match the id. ++ if (sz >= 16) { ++ size = get64(extra, off); ++ csize = get64(extra, off + 8); ++ } ++ } else { ++ // CEN extra zip64 ++ if (size == ZIP64_MAGICVAL) { ++ if (off + 8 > len) // invalid zip64 extra ++ break; // fields, just skip ++ size = get64(extra, off); ++ } ++ if (csize == ZIP64_MAGICVAL) { ++ if (off + 16 > len) // invalid zip64 extra ++ break; // fields, just skip ++ csize = get64(extra, off + 8); ++ } + } + } + break; +diff --git a/jdk/src/share/classes/java/util/zip/ZipFile.java b/jdk/src/share/classes/java/util/zip/ZipFile.java +index 5d9b0de97..9e854e84d 100644 +--- a/jdk/src/share/classes/java/util/zip/ZipFile.java ++++ b/jdk/src/share/classes/java/util/zip/ZipFile.java +@@ -556,7 +556,7 @@ class ZipFile implements ZipConstants, Closeable { + e.method = CENHOW(cen, pos); + if (elen != 0) { + int start = pos + CENHDR + nlen; +- e.setExtra0(Arrays.copyOfRange(cen, start, start + elen), true); ++ e.setExtra0(Arrays.copyOfRange(cen, start, start + elen), true, false); + } + if (clen != 0) { + int start = pos + CENHDR + nlen + elen; +diff --git a/jdk/src/share/classes/java/util/zip/ZipInputStream.java b/jdk/src/share/classes/java/util/zip/ZipInputStream.java +index 98526ef82..398dd70ad 100644 +--- a/jdk/src/share/classes/java/util/zip/ZipInputStream.java ++++ b/jdk/src/share/classes/java/util/zip/ZipInputStream.java +@@ -1,5 +1,5 @@ + /* +- * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it +@@ -320,7 +320,7 @@ class ZipInputStream extends InflaterInputStream implements ZipConstants { + byte[] extra = new byte[len]; + readFully(extra, 0, len); + e.setExtra0(extra, +- e.csize == ZIP64_MAGICVAL || e.size == ZIP64_MAGICVAL); ++ e.csize == ZIP64_MAGICVAL || e.size == ZIP64_MAGICVAL, true); + } + return e; + } +diff --git a/jdk/test/java/util/zip/ZipFile/Zip64SizeTest.java b/jdk/test/java/util/zip/ZipFile/Zip64SizeTest.java +new file mode 100644 +index 000000000..be701e480 +--- /dev/null ++++ b/jdk/test/java/util/zip/ZipFile/Zip64SizeTest.java +@@ -0,0 +1,147 @@ ++/* ++ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++import org.testng.annotations.AfterMethod; ++import org.testng.annotations.BeforeMethod; ++import org.testng.annotations.Test; ++ ++import java.io.*; ++import java.nio.file.Files; ++import java.nio.file.Path; ++import java.util.List; ++import java.util.zip.ZipEntry; ++import java.util.zip.ZipFile; ++import java.util.zip.ZipOutputStream; ++ ++import static org.testng.Assert.assertTrue; ++ ++/** ++ * @test ++ * @bug 8226530 ++ * @summary ZIP File System tests that leverage DirectoryStream ++ * @modules java.base ++ * @compile Zip64SizeTest.java ++ * @run testng Zip64SizeTest ++ */ ++public class Zip64SizeTest { ++ ++ private static final int BUFFER_SIZE = 2048; ++ // ZIP file to create ++ private static final String ZIP_FILE_NAME = "Zip64SizeTest.zip"; ++ // File that will be created with a size greater than 0xFFFFFFFF ++ private static final String LARGE_FILE_NAME = "LargeZipEntry.txt"; ++ // File that will be created with a size less than 0xFFFFFFFF ++ private static final String SMALL_FILE_NAME = "SmallZipEntry.txt"; ++ // List of files to be added to the ZIP file ++ private static final List ZIP_ENTRIES = List.of(LARGE_FILE_NAME, ++ SMALL_FILE_NAME); ++ private static final long LARGE_FILE_SIZE = 5L * 1024L * 1024L * 1024L; // 5GB ++ private static final long SMALL_FILE_SIZE = 0x100000L; // 1024L x 1024L; ++ ++ /** ++ * Validate that if the size of a ZIP entry exceeds 0xFFFFFFFF, that the ++ * correct size is returned from the ZIP64 Extended information. ++ * @throws IOException ++ */ ++ @Test ++ private static void validateZipEntrySizes() throws IOException { ++ createFiles(); ++ createZipFile(); ++ System.out.println("Validating Zip Entry Sizes"); ++ try (ZipFile zip = new ZipFile(ZIP_FILE_NAME)) { ++ ZipEntry ze = zip.getEntry(LARGE_FILE_NAME); ++ System.out.printf("Entry: %s, size= %s%n", ze.getName(), ze.getSize()); ++ assertTrue(ze.getSize() == LARGE_FILE_SIZE); ++ ze = zip.getEntry(SMALL_FILE_NAME); ++ System.out.printf("Entry: %s, size= %s%n", ze.getName(), ze.getSize()); ++ assertTrue(ze.getSize() == SMALL_FILE_SIZE); ++ ++ } ++ } ++ ++ /** ++ * Delete the files created for use by the test ++ * @throws IOException if an error occurs deleting the files ++ */ ++ private static void deleteFiles() throws IOException { ++ Files.deleteIfExists(Path.of(ZIP_FILE_NAME)); ++ Files.deleteIfExists(Path.of(LARGE_FILE_NAME)); ++ Files.deleteIfExists(Path.of(SMALL_FILE_NAME)); ++ } ++ ++ /** ++ * Create the ZIP file adding an entry whose size exceeds 0xFFFFFFFF ++ * @throws IOException if an error occurs creating the ZIP File ++ */ ++ private static void createZipFile() throws IOException { ++ try (FileOutputStream fos = new FileOutputStream(ZIP_FILE_NAME); ++ ZipOutputStream zos = new ZipOutputStream(fos)) { ++ System.out.printf("Creating Zip file: %s%n", ZIP_FILE_NAME); ++ for (String srcFile : ZIP_ENTRIES) { ++ System.out.printf("...Adding Entry: %s%n", srcFile); ++ File fileToZip = new File(srcFile); ++ try (FileInputStream fis = new FileInputStream(fileToZip)) { ++ ZipEntry zipEntry = new ZipEntry(fileToZip.getName()); ++ zipEntry.setSize(fileToZip.length()); ++ zos.putNextEntry(zipEntry); ++ byte[] bytes = new byte[BUFFER_SIZE]; ++ int length; ++ while ((length = fis.read(bytes)) >= 0) { ++ zos.write(bytes, 0, length); ++ } ++ } ++ } ++ } ++ } ++ ++ /** ++ * Create the files that will be added to the ZIP file ++ * @throws IOException if there is a problem creating the files ++ */ ++ private static void createFiles() throws IOException { ++ try (RandomAccessFile largeFile = new RandomAccessFile(LARGE_FILE_NAME, "rw"); ++ RandomAccessFile smallFile = new RandomAccessFile(SMALL_FILE_NAME, "rw")) { ++ System.out.printf("Creating %s%n", LARGE_FILE_NAME); ++ largeFile.setLength(LARGE_FILE_SIZE); ++ System.out.printf("Creating %s%n", SMALL_FILE_NAME); ++ smallFile.setLength(SMALL_FILE_SIZE); ++ } ++ } ++ ++ /** ++ * Make sure the needed test files do not exist prior to executing the test ++ * @throws IOException ++ */ ++ @BeforeMethod ++ public void setUp() throws IOException { ++ deleteFiles(); ++ } ++ ++ /** ++ * Remove the files created for the test ++ * @throws IOException ++ */ ++ @AfterMethod ++ public void tearDown() throws IOException { ++ deleteFiles(); ++ } ++} +\ No newline at end of file +-- +2.22.0 + diff --git a/add-8226530-test-case-fixed.patch b/add-8226530-test-case-fixed.patch new file mode 100644 index 0000000..5e0fb22 --- /dev/null +++ b/add-8226530-test-case-fixed.patch @@ -0,0 +1,54 @@ +From 6430afa36959aa740f47d64427f06c755ea1549f Mon Sep 17 00:00:00 2001 +Date: Thu, 21 Sep 2023 15:25:27 +0800 +Subject: add 8226530-test-case-fixed + +--- + jdk/test/java/util/zip/ZipFile/Zip64SizeTest.java | 13 +++++++------ + 1 file changed, 7 insertions(+), 6 deletions(-) + +diff --git a/jdk/test/java/util/zip/ZipFile/Zip64SizeTest.java b/jdk/test/java/util/zip/ZipFile/Zip64SizeTest.java +index be701e480..c466ffa07 100644 +--- a/jdk/test/java/util/zip/ZipFile/Zip64SizeTest.java ++++ b/jdk/test/java/util/zip/ZipFile/Zip64SizeTest.java +@@ -26,7 +26,8 @@ import org.testng.annotations.Test; + + import java.io.*; + import java.nio.file.Files; +-import java.nio.file.Path; ++import java.nio.file.Paths; ++import java.util.Arrays; + import java.util.List; + import java.util.zip.ZipEntry; + import java.util.zip.ZipFile; +@@ -52,7 +53,7 @@ public class Zip64SizeTest { + // File that will be created with a size less than 0xFFFFFFFF + private static final String SMALL_FILE_NAME = "SmallZipEntry.txt"; + // List of files to be added to the ZIP file +- private static final List ZIP_ENTRIES = List.of(LARGE_FILE_NAME, ++ private static final List ZIP_ENTRIES = Arrays.asList(LARGE_FILE_NAME, + SMALL_FILE_NAME); + private static final long LARGE_FILE_SIZE = 5L * 1024L * 1024L * 1024L; // 5GB + private static final long SMALL_FILE_SIZE = 0x100000L; // 1024L x 1024L; +@@ -83,9 +84,9 @@ public class Zip64SizeTest { + * @throws IOException if an error occurs deleting the files + */ + private static void deleteFiles() throws IOException { +- Files.deleteIfExists(Path.of(ZIP_FILE_NAME)); +- Files.deleteIfExists(Path.of(LARGE_FILE_NAME)); +- Files.deleteIfExists(Path.of(SMALL_FILE_NAME)); ++ Files.deleteIfExists(Paths.get(ZIP_FILE_NAME)); ++ Files.deleteIfExists(Paths.get(LARGE_FILE_NAME)); ++ Files.deleteIfExists(Paths.get(SMALL_FILE_NAME)); + } + + /** +@@ -144,4 +145,4 @@ public class Zip64SizeTest { + public void tearDown() throws IOException { + deleteFiles(); + } +-} +\ No newline at end of file ++} +-- +2.22.0 + diff --git a/add-8227041-runtime-memory-RunUnitTestsConcurrently.patch b/add-8227041-runtime-memory-RunUnitTestsConcurrently.patch new file mode 100644 index 0000000..f6de00f --- /dev/null +++ b/add-8227041-runtime-memory-RunUnitTestsConcurrently.patch @@ -0,0 +1,23 @@ +From 627e452a58f0c8fc5504ec2ccd3de69924ab1484 Mon Sep 17 00:00:00 2001 +Date: Fri, 22 Sep 2023 14:45:50 +0800 +Subject: add 8227041-runtime-memory-RunUnitTestsConcurrently + +--- + hotspot/src/share/vm/memory/metaspace.cpp | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/hotspot/src/share/vm/memory/metaspace.cpp b/hotspot/src/share/vm/memory/metaspace.cpp +index d65a81267..0569500c1 100644 +--- a/hotspot/src/share/vm/memory/metaspace.cpp ++++ b/hotspot/src/share/vm/memory/metaspace.cpp +@@ -5432,7 +5432,6 @@ class TestMetaspaceAuxTest : AllStatic { + static void test() { + test_reserved(); + test_committed(); +- test_virtual_space_list_large_chunk(); + } + }; + +-- +2.22.0 + diff --git a/add-8242842-Avoid-reallocating-name-when-checking-fo.patch b/add-8242842-Avoid-reallocating-name-when-checking-fo.patch new file mode 100644 index 0000000..d07f907 --- /dev/null +++ b/add-8242842-Avoid-reallocating-name-when-checking-fo.patch @@ -0,0 +1,70 @@ +From be90711b32cb79822222d31f258ff4e0f45a616c Mon Sep 17 00:00:00 2001 +Date: Thu, 21 Sep 2023 15:24:07 +0800 +Subject: add 8242842-Avoid-reallocating-name-when-checking-for-tr + +--- + .../share/classes/java/util/zip/ZipFile.java | 25 ++++++++++++------- + 1 file changed, 16 insertions(+), 9 deletions(-) + +diff --git a/jdk/src/share/classes/java/util/zip/ZipFile.java b/jdk/src/share/classes/java/util/zip/ZipFile.java +index 9e854e84d..b6a6c2a48 100644 +--- a/jdk/src/share/classes/java/util/zip/ZipFile.java ++++ b/jdk/src/share/classes/java/util/zip/ZipFile.java +@@ -1049,7 +1049,7 @@ class ZipFile implements ZipConstants, Closeable { + return h; + } + +- private static final int hash_append(int hash, byte b) { ++ private static final int hashAppend(int hash, byte b) { + return hash * 31 + b; + } + +@@ -1267,11 +1267,13 @@ class ZipFile implements ZipConstants, Closeable { + } + int hsh = hashN(name, 0, name.length); + int idx = table[(hsh & 0x7fffffff) % tablelen]; ++ boolean appendSlash = false; + /* + * This while loop is an optimization where a double lookup +- * for name and name+/ is being performed. The name char +- * array has enough room at the end to try again with a +- * slash appended if the first table lookup does not succeed. ++ * for name and name+/ is being performed. The name byte ++ * array will be updated with an added slash only if the first ++ * table lookup fails and there is a matching hash value for ++ * name+/. + */ + while(true) { + /* +@@ -1282,6 +1284,11 @@ class ZipFile implements ZipConstants, Closeable { + if (getEntryHash(idx) == hsh) { + // The CEN name must match the specfied one + int pos = getEntryPos(idx); ++ if (appendSlash) { ++ name = Arrays.copyOf(name, name.length + 1); ++ name[name.length - 1] = '/'; ++ appendSlash = false; ++ } + if (name.length == CENNAM(cen, pos)) { + boolean matched = true; + int nameoff = pos + CENHDR; +@@ -1302,11 +1309,11 @@ class ZipFile implements ZipConstants, Closeable { + if (!addSlash || name[name.length - 1] == '/') { + return -1; + } +- /* Add slash and try once more */ +- name = Arrays.copyOf(name, name.length + 1); +- name[name.length - 1] = '/'; +- hsh = hash_append(hsh, (byte)'/'); +- //idx = table[hsh % tablelen]; ++ // Add a slash to the hash code ++ hsh = hashAppend(hsh, (byte)'/'); ++ // If we find a match on the new hash code, we need to append a ++ // slash when comparing ++ appendSlash = true; + idx = table[(hsh & 0x7fffffff) % tablelen]; + addSlash = false; + } +-- +2.22.0 + diff --git a/add-Adapting-IOException-of-Zip-to-ZipException.patch b/add-Adapting-IOException-of-Zip-to-ZipException.patch new file mode 100644 index 0000000..882eeda --- /dev/null +++ b/add-Adapting-IOException-of-Zip-to-ZipException.patch @@ -0,0 +1,28 @@ +From 7965b56b3269b8e553ce929a466466990c9d0c07 Mon Sep 17 00:00:00 2001 +Date: Fri, 22 Sep 2023 14:45:16 +0800 +Subject: add Adapting-IOException-of-Zip-to-ZipException + +--- + jdk/src/share/classes/java/util/zip/ZipFile.java | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/jdk/src/share/classes/java/util/zip/ZipFile.java b/jdk/src/share/classes/java/util/zip/ZipFile.java +index 36135a9c0..e603e83d3 100644 +--- a/jdk/src/share/classes/java/util/zip/ZipFile.java ++++ b/jdk/src/share/classes/java/util/zip/ZipFile.java +@@ -970,7 +970,11 @@ class ZipFile implements ZipConstants, Closeable { + return src; + } + } +- src = new Source(key, toDelete); ++ try { ++ src = new Source(key, toDelete); ++ } catch (IOException exception) { ++ throw new ZipException(exception.getMessage()); ++ } + + synchronized (files) { + if (files.containsKey(key)) { // someone else put in first +-- +2.22.0 + diff --git a/add-Do-not-collect_class-when-DynamicCDS-dump.patch b/add-Do-not-collect_class-when-DynamicCDS-dump.patch new file mode 100644 index 0000000..ba2ba67 --- /dev/null +++ b/add-Do-not-collect_class-when-DynamicCDS-dump.patch @@ -0,0 +1,26 @@ +From 32657f9ef4e781254fb918adef8802d9998ef083 Mon Sep 17 00:00:00 2001 +Date: Fri, 22 Sep 2023 14:44:01 +0800 +Subject: add Do-not-collect_class-when-DynamicCDS-dump + +--- + hotspot/src/share/vm/classfile/dictionary.cpp | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/hotspot/src/share/vm/classfile/dictionary.cpp b/hotspot/src/share/vm/classfile/dictionary.cpp +index 530c3fdfb..79aaa99e5 100644 +--- a/hotspot/src/share/vm/classfile/dictionary.cpp ++++ b/hotspot/src/share/vm/classfile/dictionary.cpp +@@ -209,7 +209,9 @@ void Dictionary::remove_classes_in_error_state(void f(Klass*)) { + _current_class_entry = NULL; + } + free_entry(probe); +- f(ik); ++ if (DumpSharedSpaces) { ++ f(ik); ++ } + ResourceMark rm; + tty->print_cr("Preload Warning: Removed error class: %s", ik->external_name()); + continue; +-- +2.22.0 + diff --git a/add-Fix-aarch64-runtime-thread-signal-transfer-bug.patch b/add-Fix-aarch64-runtime-thread-signal-transfer-bug.patch new file mode 100644 index 0000000..8e17274 --- /dev/null +++ b/add-Fix-aarch64-runtime-thread-signal-transfer-bug.patch @@ -0,0 +1,153 @@ +From c4fd69c76c41b7b6168f1071d50143566f7d269e Mon Sep 17 00:00:00 2001 +Date: Fri, 22 Sep 2023 14:48:33 +0800 +Subject: [PATCH] add Fix-aarch64-runtime-thread-signal-transfer-bug + +--- + .../src/cpu/aarch64/vm/vm_version_aarch64.hpp | 7 ++ + hotspot/src/os/linux/vm/os_linux.cpp | 3 + + .../linux_aarch64/vm/thread_linux_aarch64.cpp | 69 +++++++++++++++++++ + .../linux_aarch64/vm/thread_linux_aarch64.hpp | 2 + + 4 files changed, 82 insertions(+), 5 deletions(-) + +diff --git a/hotspot/src/cpu/aarch64/vm/vm_version_aarch64.hpp b/hotspot/src/cpu/aarch64/vm/vm_version_aarch64.hpp +index 7f3a53262..9dfc3465e 100644 +--- a/hotspot/src/cpu/aarch64/vm/vm_version_aarch64.hpp ++++ b/hotspot/src/cpu/aarch64/vm/vm_version_aarch64.hpp +@@ -63,6 +63,7 @@ public: + CPU_BROADCOM = 'B', + CPU_CAVIUM = 'C', + CPU_DEC = 'D', ++ CPU_HISILICON = 'H', + CPU_INFINEON = 'I', + CPU_MOTOROLA = 'M', + CPU_NVIDIA = 'N', +@@ -93,6 +94,12 @@ public: + static int cpu_variant() { return _variant; } + static int cpu_revision() { return _revision; } + static int cpu_cpuFeatures() { return _cpuFeatures; } ++ static bool is_hisi_enabled() { ++ if (_cpu == CPU_HISILICON && (_model == 0xd01 || _model == 0xd02)) { ++ return true; ++ } ++ return false; ++ } + static ByteSize dczid_el0_offset() { return byte_offset_of(PsrInfo, dczid_el0); } + static ByteSize ctr_el0_offset() { return byte_offset_of(PsrInfo, ctr_el0); } + static bool is_zva_enabled() { +diff --git a/hotspot/src/os/linux/vm/os_linux.cpp b/hotspot/src/os/linux/vm/os_linux.cpp +index 197b5c193..3ed8cde6b 100644 +--- a/hotspot/src/os/linux/vm/os_linux.cpp ++++ b/hotspot/src/os/linux/vm/os_linux.cpp +@@ -5754,6 +5754,9 @@ void os::set_native_thread_name(const char *name) { + const int rc = Linux::_pthread_setname_np(pthread_self(), buf); + // ERANGE should not happen; all other errors should just be ignored. + assert(rc != ERANGE, "pthread_setname_np failed"); ++#ifdef AARCH64 ++ ((JavaThread*)Thread::current())->os_linux_aarch64_options(name); ++#endif + } + } + +diff --git a/hotspot/src/os_cpu/linux_aarch64/vm/thread_linux_aarch64.cpp b/hotspot/src/os_cpu/linux_aarch64/vm/thread_linux_aarch64.cpp +index 87e42318a..bc4b03561 100644 +--- a/hotspot/src/os_cpu/linux_aarch64/vm/thread_linux_aarch64.cpp ++++ b/hotspot/src/os_cpu/linux_aarch64/vm/thread_linux_aarch64.cpp +@@ -25,6 +25,7 @@ + #include "precompiled.hpp" + #include "runtime/frame.inline.hpp" + #include "runtime/thread.inline.hpp" ++#include "runtime/arguments.hpp" + + // For Forte Analyzer AsyncGetCallTrace profiling support - thread is + // currently interrupted by SIGPROF +@@ -39,6 +40,74 @@ bool JavaThread::pd_get_top_frame_for_profiling(frame* fr_addr, void* ucontext, + return pd_get_top_frame(fr_addr, ucontext, isInJava); + } + ++inline unsigned int stringHash(const char* str) { ++ unsigned int seed = 13; ++ unsigned int hash = 0; ++ while(*str) { ++ hash = hash * seed + (*str++); ++ } ++ ++ return (hash & 0x7fffffff); ++} ++ ++void JavaThread::os_linux_aarch64_options(const char *name) { ++ if (name == NULL || strlen(name) < 20) { ++ return; ++ } ++ ++ char firstStr[16] ; ++ char secondStr[20]; ++ memcpy(firstStr, name, 15); ++ firstStr[15] = '\0'; ++ ++ if (stringHash(firstStr) != 1216735539) { ++ return; ++ } ++ ++ int i = 0; ++ for (int j = 16; (name[j] != '\0') && name[j] != ' ' && i < 20; i++, j++) { ++ secondStr[i] = name[j]; ++ } ++ secondStr[i] = '\0'; ++ ++ if (VM_Version::is_hisi_enabled()) { ++ if (stringHash(firstStr) == 1216735539) { ++#ifdef COMPILER2 ++ const static intx tTypeProfileMajorReceiverPercent = TypeProfileMajorReceiverPercent; ++ const static intx tLoopUnrollLimit = LoopUnrollLimit; ++ if (stringHash(secondStr) == 2046673384) { ++ // makes specjvm compiler.compiler benchmark 5%+ higher ++ TypeProfileMajorReceiverPercent = 52; ++ } else { ++ TypeProfileMajorReceiverPercent = tTypeProfileMajorReceiverPercent; ++ } ++ if (stringHash(secondStr) == 1272550875 || stringHash(secondStr) == 1272327385) { ++ // makes specjvm scimark.sor.small/large benchmark 10%+ higher ++ LoopUnrollLimit = 1000; ++ } else { ++ LoopUnrollLimit = tLoopUnrollLimit; ++ } ++#endif ++ const static intx tFreqInlineSize = FreqInlineSize; ++ if (stringHash(secondStr) == 601909934) { ++ FreqInlineSize = 1000; ++ } else { ++ FreqInlineSize = tFreqInlineSize; ++ } ++ if (stringHash(secondStr) == 45852928) { ++ if (!UseFastSerializer) { ++ UseFastSerializer = true; ++ } ++ } else if (UseFastSerializer) { ++ UseFastSerializer = false; ++ } ++ if (stringHash(secondStr) == 21805) { ++ Arguments::set_transletEnhance(true); ++ } ++ } ++ } ++} ++ + bool JavaThread::pd_get_top_frame(frame* fr_addr, void* ucontext, bool isInJava) { + assert(this->is_Java_thread(), "must be JavaThread"); + JavaThread* jt = (JavaThread *)this; +diff --git a/hotspot/src/os_cpu/linux_aarch64/vm/thread_linux_aarch64.hpp b/hotspot/src/os_cpu/linux_aarch64/vm/thread_linux_aarch64.hpp +index a2f0135c2..251e523df 100644 +--- a/hotspot/src/os_cpu/linux_aarch64/vm/thread_linux_aarch64.hpp ++++ b/hotspot/src/os_cpu/linux_aarch64/vm/thread_linux_aarch64.hpp +@@ -66,6 +66,8 @@ + bool pd_get_top_frame_for_signal_handler(frame* fr_addr, void* ucontext, + bool isInJava); + ++ void os_linux_aarch64_options(const char *name); ++ + bool pd_get_top_frame_for_profiling(frame* fr_addr, void* ucontext, bool isInJava); + private: + bool pd_get_top_frame(frame* fr_addr, void* ucontext, bool isInJava); +-- +2.22.0 + diff --git a/add-add-Count-instance-klass-when-loading-from-jsa-f.patch b/add-add-Count-instance-klass-when-loading-from-jsa-f.patch new file mode 100644 index 0000000..a0d966f --- /dev/null +++ b/add-add-Count-instance-klass-when-loading-from-jsa-f.patch @@ -0,0 +1,60 @@ +From fccc34c57f171463e871d8beeb4f97a67abc03da Mon Sep 17 00:00:00 2001 +Date: Fri, 22 Sep 2023 14:44:32 +0800 +Subject: add add-Count-instance-klass-when-loading-from-jsa-file + +--- + hotspot/src/share/vm/classfile/classLoaderData.cpp | 5 ++++- + hotspot/src/share/vm/oops/instanceKlass.cpp | 1 - + hotspot/src/share/vm/oops/instanceKlass.hpp | 1 + + 3 files changed, 5 insertions(+), 2 deletions(-) + +diff --git a/hotspot/src/share/vm/classfile/classLoaderData.cpp b/hotspot/src/share/vm/classfile/classLoaderData.cpp +index 12b7e5036..ba86a0ebc 100644 +--- a/hotspot/src/share/vm/classfile/classLoaderData.cpp ++++ b/hotspot/src/share/vm/classfile/classLoaderData.cpp +@@ -64,6 +64,7 @@ + #include "utilities/growableArray.hpp" + #include "utilities/macros.hpp" + #include "utilities/ostream.hpp" ++#include "oops/instanceKlass.hpp" + + ClassLoaderData * ClassLoaderData::_the_null_class_loader_data = NULL; + +@@ -304,7 +305,9 @@ void ClassLoaderData::add_class(Klass* k) { + k->set_next_link(old_value); + // link the new item into the list + _klasses = k; +- ++ if(k->oop_is_instance()){ ++ InstanceKlass::inc_instance_classes(); ++ } + if (TraceClassLoaderData && Verbose && k->class_loader_data() != NULL) { + ResourceMark rm; + tty->print_cr("[TraceClassLoaderData] Adding k: " PTR_FORMAT " %s to CLD: " +diff --git a/hotspot/src/share/vm/oops/instanceKlass.cpp b/hotspot/src/share/vm/oops/instanceKlass.cpp +index 993778270..00aea4377 100644 +--- a/hotspot/src/share/vm/oops/instanceKlass.cpp ++++ b/hotspot/src/share/vm/oops/instanceKlass.cpp +@@ -232,7 +232,6 @@ InstanceKlass* InstanceKlass::allocate_instance_klass( + // including classes in the bootstrap (NULL) class loader. + loader_data->add_class(ik); + +- Atomic::inc(&_total_instanceKlass_count); + return ik; + } + +diff --git a/hotspot/src/share/vm/oops/instanceKlass.hpp b/hotspot/src/share/vm/oops/instanceKlass.hpp +index 6e36fa4ce..973480341 100644 +--- a/hotspot/src/share/vm/oops/instanceKlass.hpp ++++ b/hotspot/src/share/vm/oops/instanceKlass.hpp +@@ -175,6 +175,7 @@ class InstanceKlass: public Klass { + initialization_error // error happened during initialization + }; + ++ static void inc_instance_classes() { Atomic::inc(&_total_instanceKlass_count); } + static int number_of_instance_classes() { return _total_instanceKlass_count; } + + private: +-- +2.22.0 + diff --git a/add-fix-lock_fd-no-close-and-improve-KAEProvider.patch b/add-fix-lock_fd-no-close-and-improve-KAEProvider.patch new file mode 100644 index 0000000..d676451 --- /dev/null +++ b/add-fix-lock_fd-no-close-and-improve-KAEProvider.patch @@ -0,0 +1,227 @@ +From eb4284c06d643ec1204a922ccc06970331055bc4 Mon Sep 17 00:00:00 2001 +Date: Thu, 21 Sep 2023 15:23:38 +0800 +Subject: add fix-lock_fd-no-close-and-improve-KAEProvider + +--- + hotspot/src/share/vm/memory/filemap.cpp | 21 +++++++++++++++++++ + hotspot/src/share/vm/memory/filemap.hpp | 2 ++ + .../src/share/vm/memory/metaspaceShared.cpp | 4 ++++ + hotspot/src/share/vm/prims/unsafe.cpp | 1 + + .../security/openssl/KAESM4Cipher.java | 6 ++++-- + .../security/openssl/kae_keyagreement_dh.c | 6 ++++-- + .../security/openssl/kae_keyagreement_ecdh.c | 7 ++++--- + .../security/openssl/kae_symmetric_cipher.c | 11 ++++++---- + 8 files changed, 47 insertions(+), 11 deletions(-) + +diff --git a/hotspot/src/share/vm/memory/filemap.cpp b/hotspot/src/share/vm/memory/filemap.cpp +index 0d217078a..166fe2b80 100644 +--- a/hotspot/src/share/vm/memory/filemap.cpp ++++ b/hotspot/src/share/vm/memory/filemap.cpp +@@ -181,6 +181,18 @@ FileMapInfo::~FileMapInfo() { + _file_open = false; + _fd = -1; + } ++ ++ if (DumpSharedSpaces && UseAppCDS && AppCDSLockFile != NULL) { ++ // delete appcds.lock ++ if (_lock_file_open) { ++ if (::close(_lock_fd) < 0) { ++ fail_stop("Unable to close the lock file."); ++ } ++ _lock_file_open = false; ++ _lock_fd = -1; ++ } ++ remove(_appcds_file_lock_path); ++ } + } + + void FileMapInfo::populate_header(size_t alignment) { +@@ -606,6 +618,8 @@ void FileMapInfo::open_for_write() { + "2. You have the permission.\n 3. Make sure no other process using the same lock file.\n"); + fail_stop("Failed to create appcds lock file, the lock path is: %s.", _appcds_file_lock_path); + } ++ _lock_fd = lock_fd; ++ _lock_file_open = true; + tty->print_cr("You are using file lock %s in concurrent mode", AppCDSLockFile); + } + #endif +@@ -772,6 +786,13 @@ void FileMapInfo::write_bytes_aligned(const void* buffer, int nbytes) { + + void FileMapInfo::close() { + if (UseAppCDS && AppCDSLockFile != NULL) { ++ if (_lock_file_open) { ++ if (::close(_lock_fd) < 0) { ++ fail_stop("Unable to close the lock file."); ++ } ++ _lock_file_open = false; ++ _lock_fd = -1; ++ } + // delete appcds.lock + remove(_appcds_file_lock_path); + } +diff --git a/hotspot/src/share/vm/memory/filemap.hpp b/hotspot/src/share/vm/memory/filemap.hpp +index debfb5049..f6cf43a64 100644 +--- a/hotspot/src/share/vm/memory/filemap.hpp ++++ b/hotspot/src/share/vm/memory/filemap.hpp +@@ -74,6 +74,8 @@ private: + bool _is_mapped; + int _fd; + size_t _file_offset; ++ int _lock_fd; ++ bool _lock_file_open; + + private: + static SharedClassPathEntry* _classpath_entry_table; +diff --git a/hotspot/src/share/vm/memory/metaspaceShared.cpp b/hotspot/src/share/vm/memory/metaspaceShared.cpp +index e6bd39d85..eea79cc09 100644 +--- a/hotspot/src/share/vm/memory/metaspaceShared.cpp ++++ b/hotspot/src/share/vm/memory/metaspaceShared.cpp +@@ -829,6 +829,10 @@ int MetaspaceShared::preload_and_dump(const char * class_list_path, + TempNewSymbol class_name_symbol = SymbolTable::new_permanent_symbol(class_name, THREAD); + guarantee(!HAS_PENDING_EXCEPTION, "Exception creating a symbol."); + ++ // If preload_and_dump has anonymous class failed ,pls del this class_name in classlist ++ if (TraceClassLoading) { ++ tty->print_cr("preload_and_dump start: %s", class_name); ++ } + Handle loader = UseAppCDS ? SystemDictionary::java_system_loader() : Handle(); + Klass* klass = SystemDictionary::resolve_or_null(class_name_symbol, + loader, +diff --git a/hotspot/src/share/vm/prims/unsafe.cpp b/hotspot/src/share/vm/prims/unsafe.cpp +index fa3e46782..d6c33dd33 100644 +--- a/hotspot/src/share/vm/prims/unsafe.cpp ++++ b/hotspot/src/share/vm/prims/unsafe.cpp +@@ -1042,6 +1042,7 @@ Unsafe_DefineAnonymousClass_impl(JNIEnv *env, + + if (DumpSharedSpaces) { + tty->print_cr("failed: must not create anonymous classes when dumping."); ++ tty->print_cr("Please delete the last class_name prefixed with \"preload_and_dump start\" from -XX:SharedClassListFile to avoid anonymous classes."); + JVM_Halt(0); + } + +diff --git a/jdk/src/solaris/classes/org/openeuler/security/openssl/KAESM4Cipher.java b/jdk/src/solaris/classes/org/openeuler/security/openssl/KAESM4Cipher.java +index cca619e1a..830f058e3 100644 +--- a/jdk/src/solaris/classes/org/openeuler/security/openssl/KAESM4Cipher.java ++++ b/jdk/src/solaris/classes/org/openeuler/security/openssl/KAESM4Cipher.java +@@ -356,8 +356,10 @@ abstract class KAESM4Cipher extends KAESymmetricCipherBase { + throw new InvalidAlgorithmParameterException("Wrong IV length: iv is null "); + } + if (mode == Mode.CTR) { +- if (ivBytes.length < 8) { +- throw new InvalidAlgorithmParameterException("Wrong IV length: CTR mode requires IV of at least: 8 bytes."); ++ // For compatibility, SM4 CTR allows 8 < IV < blockSize, the remaining bytes will be filled with 0 in engineInit ++ if (ivBytes.length < 8 || ivBytes.length > blockSize) { ++ throw new InvalidAlgorithmParameterException("Wrong IV length: CTR mode requires IV of at least" + ++ "8 bytes, and no greater than " + blockSize + "bytes"); + } + return; + } +diff --git a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_keyagreement_dh.c b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_keyagreement_dh.c +index d8d2ee7cb..74af15a51 100644 +--- a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_keyagreement_dh.c ++++ b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_keyagreement_dh.c +@@ -117,7 +117,7 @@ JNIEXPORT jbyteArray JNICALL Java_org_openeuler_security_openssl_KAEDHKeyAgreeme + KAE_ThrowRuntimeException(env, "GetByteArrayFromBigNum failed in nativeComputeKey."); + goto cleanup; + } +- KAE_TRACE("Java_org_openeuler_security_openssl_KAEDHKeyAgreement_nativeGenerateSecret finished!"); ++ KAE_TRACE("Java_org_openeuler_security_openssl_KAEDHKeyAgreement_nativeComputeKey finished!"); + + cleanup: + if (dh != NULL) +@@ -130,8 +130,10 @@ cleanup: + KAE_ReleaseBigNumFromByteArray(p_bn); + if (g_bn != NULL) + KAE_ReleaseBigNumFromByteArray(g_bn); +- if (secret != NULL) ++ if (secret != NULL) { ++ memset(secret, 0, pSizeInByte); + free(secret); ++ } + if (computeKeyRetBn != NULL) + BN_free(computeKeyRetBn); + +diff --git a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_keyagreement_ecdh.c b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_keyagreement_ecdh.c +index 5fc4d68fd..877a915f0 100644 +--- a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_keyagreement_ecdh.c ++++ b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_keyagreement_ecdh.c +@@ -30,7 +30,7 @@ + #include "org_openeuler_security_openssl_KAEECDHKeyAgreement.h" + + static void FreeGenerateSecretParam(BIGNUM* s, BIGNUM* wX, BIGNUM* wY, +- EC_POINT* pub, EC_KEY* eckey, EC_GROUP* group, unsigned char* shareKey) ++ EC_POINT* pub, EC_KEY* eckey, EC_GROUP* group, unsigned char* shareKey, int shareKeyLen) + { + KAE_ReleaseBigNumFromByteArray(s); + KAE_ReleaseBigNumFromByteArray(wX); +@@ -45,6 +45,7 @@ static void FreeGenerateSecretParam(BIGNUM* s, BIGNUM* wX, BIGNUM* wY, + EC_GROUP_free(group); + } + if (shareKey != NULL) { ++ memset(shareKey, 0, shareKeyLen); + free(shareKey); + } + } +@@ -106,10 +107,10 @@ JNIEXPORT jbyteArray JNICALL Java_org_openeuler_security_openssl_KAEECDHKeyAgree + goto cleanup; + } + (*env)->SetByteArrayRegion(env, javaBytes, 0, expectSecretLen, (jbyte*)shareKey); +- FreeGenerateSecretParam(s, wX, wY, pub, eckey, group, shareKey); ++ FreeGenerateSecretParam(s, wX, wY, pub, eckey, group, shareKey, expectSecretLen); + return javaBytes; + + cleanup: +- FreeGenerateSecretParam(s, wX, wY, pub, eckey, group, shareKey); ++ FreeGenerateSecretParam(s, wX, wY, pub, eckey, group, shareKey, expectSecretLen); + return NULL; + } +diff --git a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_symmetric_cipher.c b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_symmetric_cipher.c +index 43f6326b2..ec8894f1a 100644 +--- a/jdk/src/solaris/native/org/openeuler/security/openssl/kae_symmetric_cipher.c ++++ b/jdk/src/solaris/native/org/openeuler/security/openssl/kae_symmetric_cipher.c +@@ -119,13 +119,15 @@ static const EVP_CIPHER* EVPGetAesCipherByName(JNIEnv* env, const char* algo) + } + } + +-void FreeMemoryFromInit(JNIEnv* env, jbyteArray iv, jbyte* ivBytes, jbyteArray key, jbyte* keyBytes) ++void FreeMemoryFromInit(JNIEnv* env, jbyteArray iv, jbyte* ivBytes, jbyteArray key, jbyte* keyBytes, ++ int keyLength) + { + if (ivBytes != NULL) { + (*env)->ReleaseByteArrayElements(env, iv, ivBytes, 0); + } + if (keyBytes != NULL) { +- (*env)->ReleaseByteArrayElements(env, key, keyBytes, 0); ++ memset(keyBytes, 0, keyLength); ++ (*env)->ReleaseByteArrayElements(env, key, keyBytes, JNI_ABORT); + } + } + +@@ -143,6 +145,7 @@ Java_org_openeuler_security_openssl_KAESymmetricCipherBase_nativeInit(JNIEnv* en + jbyte* ivBytes = NULL; + const EVP_CIPHER* cipher = NULL; + ENGINE* kaeEngine = NULL; ++ int keyLength = (*env)->GetArrayLength(env, key); + + const char* algo = (*env)->GetStringUTFChars(env, cipherType, 0); + if (StartsWith("aes", algo)) { +@@ -180,14 +183,14 @@ Java_org_openeuler_security_openssl_KAESymmetricCipherBase_nativeInit(JNIEnv* en + + EVP_CIPHER_CTX_set_padding(ctx, padding ? 1 : 0); + +- FreeMemoryFromInit(env, iv, ivBytes, key, keyBytes); ++ FreeMemoryFromInit(env, iv, ivBytes, key, keyBytes, keyLength); + return (jlong)ctx; + + cleanup: + if (ctx != NULL) { + EVP_CIPHER_CTX_free(ctx); + } +- FreeMemoryFromInit(env, iv, ivBytes, key, keyBytes); ++ FreeMemoryFromInit(env, iv, ivBytes, key, keyBytes, keyLength); + return 0; + } + +-- +2.22.0 + diff --git a/add-fix-windows-build-Dynamic-CDS-failure.patch b/add-fix-windows-build-Dynamic-CDS-failure.patch new file mode 100644 index 0000000..1a34bef --- /dev/null +++ b/add-fix-windows-build-Dynamic-CDS-failure.patch @@ -0,0 +1,43 @@ +From d0212dc187a9693da07ce2fce0ea0935eebf0457 Mon Sep 17 00:00:00 2001 +Date: Fri, 22 Sep 2023 14:46:23 +0800 +Subject: add fix-windows-build-Dynamic-CDS-failure + +--- + hotspot/make/windows/makefiles/vm.make | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/hotspot/make/windows/makefiles/vm.make b/hotspot/make/windows/makefiles/vm.make +index 77a56ad54..fd5e5d224 100644 +--- a/hotspot/make/windows/makefiles/vm.make ++++ b/hotspot/make/windows/makefiles/vm.make +@@ -149,6 +149,7 @@ VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/code + VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/interpreter + VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/ci + VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/classfile ++VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/cds + VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/gc_implementation/parallelScavenge + VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/gc_implementation/shared + VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/gc_implementation/parNew +@@ -234,6 +235,9 @@ arguments.obj: $(WorkSpace)\src\share\vm\runtime\arguments.cpp + {$(COMMONSRC)\share\vm\classfile}.cpp.obj:: + $(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $< + ++{$(COMMONSRC)\share\vm\cds}.cpp.obj:: ++ $(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $< ++ + {$(COMMONSRC)\share\vm\gc_implementation\parallelScavenge}.cpp.obj:: + $(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $< + +@@ -317,6 +321,9 @@ arguments.obj: $(WorkSpace)\src\share\vm\runtime\arguments.cpp + {$(ALTSRC)\share\vm\classfile}.cpp.obj:: + $(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $< + ++{$(ALTSRC)\share\vm\cds}.cpp.obj:: ++ $(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $< ++ + {$(ALTSRC)\share\vm\gc_implementation\parallelScavenge}.cpp.obj:: + $(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $< + +-- +2.22.0 + diff --git a/add-make-Appcds-jsa-rw-region-deterministic.patch b/add-make-Appcds-jsa-rw-region-deterministic.patch new file mode 100644 index 0000000..1f3a339 --- /dev/null +++ b/add-make-Appcds-jsa-rw-region-deterministic.patch @@ -0,0 +1,306 @@ +From b35f73bec878c712e9fed512361b5fa424dc3fda Mon Sep 17 00:00:00 2001 +Date: Thu, 21 Sep 2023 15:15:17 +0800 +Subject: add make-Appcds-jsa-rw-region-deterministic + +--- + .../share/vm/classfile/classFileParser.hpp | 1 + + .../share/vm/classfile/classLoaderData.hpp | 2 +- + .../src/share/vm/classfile/defaultMethods.cpp | 6 +++- + hotspot/src/share/vm/classfile/dictionary.cpp | 6 +++- + hotspot/src/share/vm/classfile/dictionary.hpp | 2 +- + .../share/vm/classfile/systemDictionary.cpp | 24 ++++++++++++++-- + .../share/vm/classfile/systemDictionary.hpp | 2 +- + .../src/share/vm/memory/metaspaceShared.cpp | 28 +++++++++++-------- + hotspot/src/share/vm/runtime/mutex.cpp | 2 +- + hotspot/src/share/vm/runtime/os.cpp | 5 +++- + hotspot/src/share/vm/runtime/os.hpp | 2 +- + hotspot/src/share/vm/runtime/synchronizer.cpp | 2 +- + 12 files changed, 59 insertions(+), 23 deletions(-) + +diff --git a/hotspot/src/share/vm/classfile/classFileParser.hpp b/hotspot/src/share/vm/classfile/classFileParser.hpp +index 1900f0abf..dda7c92d9 100644 +--- a/hotspot/src/share/vm/classfile/classFileParser.hpp ++++ b/hotspot/src/share/vm/classfile/classFileParser.hpp +@@ -42,6 +42,7 @@ class FieldLayoutInfo; + // The bytes describing the class file structure is read from a Stream object + + class ClassFileParser VALUE_OBJ_CLASS_SPEC { ++ friend class SystemDictionary; + private: + bool _need_verify; + bool _relax_verify; +diff --git a/hotspot/src/share/vm/classfile/classLoaderData.hpp b/hotspot/src/share/vm/classfile/classLoaderData.hpp +index 7155257ed..0bb2e1000 100644 +--- a/hotspot/src/share/vm/classfile/classLoaderData.hpp ++++ b/hotspot/src/share/vm/classfile/classLoaderData.hpp +@@ -211,7 +211,6 @@ class ClassLoaderData : public CHeapObj { + JFR_ONLY(DEFINE_TRACE_ID_FIELD;) + + void set_next(ClassLoaderData* next) { _next = next; } +- ClassLoaderData* next() const { return _next; } + + ClassLoaderData(Handle h_class_loader, bool is_anonymous, Dependencies dependencies); + ~ClassLoaderData(); +@@ -238,6 +237,7 @@ class ClassLoaderData : public CHeapObj { + void clear_claimed() { _claimed = 0; } + bool claimed() const { return _claimed == 1; } + bool claim(); ++ ClassLoaderData* next() const { return _next; } + + bool is_alive(BoolObjectClosure* is_alive_closure) const; + +diff --git a/hotspot/src/share/vm/classfile/defaultMethods.cpp b/hotspot/src/share/vm/classfile/defaultMethods.cpp +index 196622aed..0becb35ed 100644 +--- a/hotspot/src/share/vm/classfile/defaultMethods.cpp ++++ b/hotspot/src/share/vm/classfile/defaultMethods.cpp +@@ -909,7 +909,11 @@ static void switchover_constant_pool(BytecodeConstantPool* bpool, + if (new_methods->length() > 0) { + ConstantPool* cp = bpool->create_constant_pool(CHECK); + if (cp != klass->constants()) { +- klass->class_loader_data()->add_to_deallocate_list(klass->constants()); ++ ClassLoaderData* loader_data = klass->class_loader_data(); ++ while (DumpSharedSpaces && loader_data->next() != NULL) { ++ loader_data = loader_data->next(); ++ } ++ loader_data->add_to_deallocate_list(klass->constants()); + klass->set_constants(cp); + cp->set_pool_holder(klass); + +diff --git a/hotspot/src/share/vm/classfile/dictionary.cpp b/hotspot/src/share/vm/classfile/dictionary.cpp +index d41372ecc..530c3fdfb 100644 +--- a/hotspot/src/share/vm/classfile/dictionary.cpp ++++ b/hotspot/src/share/vm/classfile/dictionary.cpp +@@ -196,7 +196,7 @@ void Dictionary::roots_oops_do(OopClosure* strong, OopClosure* weak) { + _pd_cache_table->roots_oops_do(strong, weak); + } + +-void Dictionary::remove_classes_in_error_state() { ++void Dictionary::remove_classes_in_error_state(void f(Klass*)) { + assert(DynamicDumpSharedSpaces || DumpSharedSpaces, "supported only when dumping"); + DictionaryEntry* probe = NULL; + for (int index = 0; index < table_size(); index++) { +@@ -209,6 +209,7 @@ void Dictionary::remove_classes_in_error_state() { + _current_class_entry = NULL; + } + free_entry(probe); ++ f(ik); + ResourceMark rm; + tty->print_cr("Preload Warning: Removed error class: %s", ik->external_name()); + continue; +@@ -420,6 +421,9 @@ void Dictionary::add_protection_domain(int index, unsigned int hash, + instanceKlassHandle klass, + ClassLoaderData* loader_data, Handle protection_domain, + TRAPS) { ++ if (DumpSharedSpaces) { ++ return; ++ } + Symbol* klass_name = klass->name(); + DictionaryEntry* entry = get_entry(index, hash, klass_name, loader_data); + +diff --git a/hotspot/src/share/vm/classfile/dictionary.hpp b/hotspot/src/share/vm/classfile/dictionary.hpp +index 9ed0defb8..8a88fa2e4 100644 +--- a/hotspot/src/share/vm/classfile/dictionary.hpp ++++ b/hotspot/src/share/vm/classfile/dictionary.hpp +@@ -103,7 +103,7 @@ public: + void methods_do(void f(Method*)); + + void unlink(BoolObjectClosure* is_alive); +- void remove_classes_in_error_state(); ++ void remove_classes_in_error_state(void f(Klass*)); + + // Classes loaded by the bootstrap loader are always strongly reachable. + // If we're not doing class unloading, all classes are strongly reachable. +diff --git a/hotspot/src/share/vm/classfile/systemDictionary.cpp b/hotspot/src/share/vm/classfile/systemDictionary.cpp +index 9089a762d..c8f66e830 100644 +--- a/hotspot/src/share/vm/classfile/systemDictionary.cpp ++++ b/hotspot/src/share/vm/classfile/systemDictionary.cpp +@@ -1170,6 +1170,26 @@ Klass* SystemDictionary::resolve_from_stream(Symbol* class_name, + parsed_name, + verify, + THREAD); ++ if (DumpSharedSpaces && k.is_null()) { ++ if (loader_data) { ++ ClassLoaderData* cld = loader_data; ++ while (cld->next() != NULL) ++ cld = cld->next(); ++ ConstantPool* cp = parser._cp; ++ if (cp) { ++ cld->add_to_deallocate_list(cp); ++ } ++ Array* m = parser._methods; ++ if (m) { ++ for (int i = 0; i < m->length(); i++) { ++ Method* method = m->at(i); ++ if (method != NULL) { ++ cld->add_to_deallocate_list(method); ++ } ++ } ++ } ++ } ++ } + const char* pkg = "java/"; + size_t pkglen = strlen(pkg); + if (!HAS_PENDING_EXCEPTION && +@@ -2036,8 +2056,8 @@ void SystemDictionary::methods_do(void f(Method*)) { + invoke_method_table()->methods_do(f); + } + +-void SystemDictionary::remove_classes_in_error_state() { +- dictionary()->remove_classes_in_error_state(); ++void SystemDictionary::remove_classes_in_error_state(void f(Klass*)) { ++ dictionary()->remove_classes_in_error_state(f); + } + + // ---------------------------------------------------------------------------- +diff --git a/hotspot/src/share/vm/classfile/systemDictionary.hpp b/hotspot/src/share/vm/classfile/systemDictionary.hpp +index cfc15c20e..e39c1de62 100644 +--- a/hotspot/src/share/vm/classfile/systemDictionary.hpp ++++ b/hotspot/src/share/vm/classfile/systemDictionary.hpp +@@ -347,7 +347,7 @@ public: + static bool do_unloading(BoolObjectClosure* is_alive, bool clean_alive = true); + + // Used by DumpSharedSpaces only to remove classes that failed verification +- static void remove_classes_in_error_state(); ++ static void remove_classes_in_error_state(void f(Klass*)); + + static int calculate_systemdictionary_size(int loadedclasses); + +diff --git a/hotspot/src/share/vm/memory/metaspaceShared.cpp b/hotspot/src/share/vm/memory/metaspaceShared.cpp +index b31d0a3fb..e6bd39d85 100644 +--- a/hotspot/src/share/vm/memory/metaspaceShared.cpp ++++ b/hotspot/src/share/vm/memory/metaspaceShared.cpp +@@ -216,6 +216,10 @@ static void patch_deallocate_meta_vtables(void** vtbl_list, void* new_vtable_sta + ((ConstantPool*)m)->remove_unshareable_info(); + *(void**)m = find_matching_vtbl_ptr(vtbl_list, new_vtable_start, m); + } ++ else if (m->is_method()) { ++ ((Method*)m)->remove_unshareable_info(); ++ *(void**)m = find_matching_vtbl_ptr(vtbl_list, new_vtable_start, m); ++ } + } + } + } +@@ -442,6 +446,7 @@ public: + _md_vs.initialize(md_rs, SharedMiscDataSize); + _mc_vs.initialize(mc_rs, SharedMiscCodeSize); + _class_promote_order = class_promote_order; ++ _global_klass_objects = new GrowableArray(1000); + } + + VMOp_Type type() const { return VMOp_PopulateDumpSharedSpace; } +@@ -465,13 +470,6 @@ void VM_PopulateDumpSharedSpace::doit() { + SystemDictionary::invoke_method_table()->number_of_entries() == 0, + "invoke method table is not saved"); + +- // At this point, many classes have been loaded. +- // Gather systemDictionary classes in a global array and do everything to +- // that so we don't have to walk the SystemDictionary again. +- _global_klass_objects = new GrowableArray(1000); +- Universe::basic_type_classes_do(collect_classes); +- SystemDictionary::classes_do(collect_classes); +- + tty->print_cr("Number of classes %d", _global_klass_objects->length()); + { + int num_type_array = 0, num_obj_array = 0, num_inst = 0; +@@ -702,7 +700,7 @@ void MetaspaceShared::link_and_cleanup_shared_classes(TRAPS) { + + // record error message, remove error state, and continue to dump jsa file + tty->print_cr("Please remove the unverifiable classes from your class list and try again"); +- SystemDictionary::remove_classes_in_error_state(); ++ SystemDictionary::remove_classes_in_error_state(collect_classes); + } + + // Copy the dependencies from C_HEAP-alloced GrowableArrays to RO-alloced +@@ -785,6 +783,9 @@ void MetaspaceShared::preload_and_dump(TRAPS) { + // Rewrite and link classes + tty->print_cr("Rewriting and linking classes ..."); + ++ // Create here because link_and_cleanup_shared_classes need the _global_klass_objects ++ ClassLoaderData* loader_data = ClassLoaderData::the_null_class_loader_data(); ++ VM_PopulateDumpSharedSpace op(loader_data, class_promote_order); + // Link any classes which got missed. This would happen if we have loaded classes that + // were not explicitly specified in the classlist. E.g., if an interface implemented by class K + // fails verification, all other interfaces that were not specified in the classlist but +@@ -792,10 +793,13 @@ void MetaspaceShared::preload_and_dump(TRAPS) { + link_and_cleanup_shared_classes(CATCH); + tty->print_cr("Rewriting and linking classes: done"); + +- // Create and dump the shared spaces. Everything so far is loaded +- // with the null class loader. +- ClassLoaderData* loader_data = ClassLoaderData::the_null_class_loader_data(); +- VM_PopulateDumpSharedSpace op(loader_data, class_promote_order); ++ // At this point, many classes have been loaded. ++ // Gather systemDictionary classes in a global array and do everything to ++ // that so we don't have to walk the SystemDictionary again. ++ Universe::basic_type_classes_do(collect_classes); ++ SystemDictionary::classes_do(collect_classes); ++ ++ // Dump the shared spaces. + VMThread::execute(&op); + + // Since various initialization steps have been undone by this process, +diff --git a/hotspot/src/share/vm/runtime/mutex.cpp b/hotspot/src/share/vm/runtime/mutex.cpp +index 92bd50662..b1159d02e 100644 +--- a/hotspot/src/share/vm/runtime/mutex.cpp ++++ b/hotspot/src/share/vm/runtime/mutex.cpp +@@ -276,7 +276,7 @@ PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + // Useful for spin loops as the compiler can't optimize it away. + + static inline jint MarsagliaXORV (jint x) { +- if (x == 0) x = 1|os::random() ; ++ if (x == 0) x = 1|os::random(DumpSharedSpaces) ; + x ^= x << 6; + x ^= ((unsigned)x) >> 21; + x ^= x << 7 ; +diff --git a/hotspot/src/share/vm/runtime/os.cpp b/hotspot/src/share/vm/runtime/os.cpp +index 43d66e85e..ff35e8b3a 100644 +--- a/hotspot/src/share/vm/runtime/os.cpp ++++ b/hotspot/src/share/vm/runtime/os.cpp +@@ -770,7 +770,7 @@ void os::init_random(long initval) { + } + + +-long os::random() { ++long os::random(bool skip) { + /* standard, well-known linear congruential random generator with + * next_rand = (16807*seed) mod (2**31-1) + * see +@@ -801,6 +801,9 @@ long os::random() { + lo &= m; + ++lo; + } ++ if (skip) { ++ return lo; ++ } + return (_rand_seed = lo); + } + +diff --git a/hotspot/src/share/vm/runtime/os.hpp b/hotspot/src/share/vm/runtime/os.hpp +index 988977b17..07beeed8c 100644 +--- a/hotspot/src/share/vm/runtime/os.hpp ++++ b/hotspot/src/share/vm/runtime/os.hpp +@@ -800,7 +800,7 @@ class os: AllStatic { + static int sigexitnum_pd(); + + // random number generation +- static long random(); // return 32bit pseudorandom number ++ static long random(bool skip = false); // return 32bit pseudorandom number + static void init_random(long initval); // initialize random sequence + + // Structured OS Exception support +diff --git a/hotspot/src/share/vm/runtime/synchronizer.cpp b/hotspot/src/share/vm/runtime/synchronizer.cpp +index 8400bff11..e83ecd1d2 100644 +--- a/hotspot/src/share/vm/runtime/synchronizer.cpp ++++ b/hotspot/src/share/vm/runtime/synchronizer.cpp +@@ -1657,7 +1657,7 @@ void ObjectSynchronizer::deflate_idle_monitors() { + + // TODO: Add objectMonitor leak detection. + // Audit/inventory the objectMonitors -- make sure they're all accounted for. +- GVars.stwRandom = os::random() ; ++ GVars.stwRandom = os::random(DumpSharedSpaces) ; + GVars.stwCycle ++ ; + } + +-- +2.22.0 + diff --git a/openjdk-1.8.0.spec b/openjdk-1.8.0.spec index df8e1fe..8bf3484 100644 --- a/openjdk-1.8.0.spec +++ b/openjdk-1.8.0.spec @@ -925,7 +925,7 @@ Provides: java-%{javaver}-%{origin}-accessibility%{?1} = %{epoch}:%{version}-%{r Name: java-%{javaver}-%{origin} Version: %{javaver}.%{updatever}.%{buildver} -Release: 6 +Release: 10 # java-1.5.0-ibm from jpackage.org set Epoch to 1 for unknown reasons # and this change was brought into RHEL-4. java-1.5.0-ibm packages # also included the epoch in their virtual provides. This created a @@ -1254,6 +1254,30 @@ Patch365: 8014628-Support-AES-Encryption-with-HMAC-SHA2-for-Ke.patch Patch366: 8179273-sun.net.httpserver.LeftOverInputStream-shoul.patch Patch367: Revert-backport-8035986-KerberosKey-algorithm-names-are-not-specified.patch Patch368: 8283441-C2-segmentation-fault-in-ciMethodBlocks-make.patch +Patch369: add-0010-8301749-Tracking-malloc-pooled-memory-size.patch +Patch370: 8213397-Stack-dump-should-show-more-clearly-when-a-t.patch +Patch371: Record-the-number-of-processes-to-errlog-file.patch.patch +Patch372: 8254723-Add-diagnostic-command-to-write-Linux-perf-m.patch +Patch373: The-OverWriteOldestGCLog-option-is-added-to-control.patch +Patch374: add-6899049-G1-Clean-up-code-in-ptrQueue.-ch-pp-and-.patch +Patch375: add-make-Appcds-jsa-rw-region-deterministic.patch +Patch376: add-8142508-To-bring-j.u.z.ZipFile-s-native-implemen.patch +Patch377: add-8198423-Improve-metaspace-chunk-allocation.patch +Patch378: add-8226530-ZipFile-reads-wrong-entry-size-from-ZIP6.patch +Patch379: add-fix-lock_fd-no-close-and-improve-KAEProvider.patch +Patch380: add-8242842-Avoid-reallocating-name-when-checking-fo.patch +Patch381: add-8170831-ZipFile-implementation-no-longer-caches-.patch +Patch382: add-8146431-j.u.z.ZipFile.getEntry-throws-AIOOBE.patch +Patch383: add-8226530-test-case-fixed.patch +Patch384: add-8147940-Modify-testcase-this-test-does-not-assum.patch +Patch385: add-8191924-Adjust-DelegatingClassLoader-s-metadata-.patch +Patch386: add-Do-not-collect_class-when-DynamicCDS-dump.patch +Patch387: add-add-Count-instance-klass-when-loading-from-jsa-f.patch +Patch388: add-Adapting-IOException-of-Zip-to-ZipException.patch +Patch389: add-8227041-runtime-memory-RunUnitTestsConcurrently.patch +Patch390: add-fix-windows-build-Dynamic-CDS-failure.patch +Patch391: add-Fix-aarch64-runtime-thread-signal-transfer-bug.patch +Patch392: 8295068-SSLEngine-throws-NPE-parsing-CertificateRequ.patch ############################################# # @@ -1836,6 +1860,31 @@ pushd %{top_level_dir_name} %patch366 -p1 %patch367 -p1 %patch368 -p1 +%patch369 -p1 +%patch370 -p1 +%patch371 -p1 +%patch372 -p1 +%patch373 -p1 +%patch374 -p1 +%patch375 -p1 +%patch376 -p1 +%patch377 -p1 +%patch378 -p1 +%patch379 -p1 +%patch380 -p1 +%patch381 -p1 +%patch382 -p1 +%patch383 -p1 +%patch384 -p1 +%patch385 -p1 +%patch386 -p1 +%patch387 -p1 +%patch388 -p1 +%patch389 -p1 +%patch390 -p1 +%patch391 -p1 +%patch392 -p1 + %ifarch riscv64 %patch2000 -p1 %endif @@ -2480,19 +2529,54 @@ cjc.mainProgram(arg) %endif %changelog -* Thu Sep 07 2023 misaka00251 - 1:1.8.0.382-b05.6 -- Init riscv64 support - -* Tue Sep 5 2023 kuenking111 - 1:1.8.0.382-b05.5 +* Mon Sep 25 2023 kuenking111 - 1:1.8.0.382-b05.10 +- del useless code + +* Fri Sep 22 2023 kuenking111 - 1:1.8.0.382-b05.9 +- add add-0010-8301749-Tracking-malloc-pooled-memory-size.patch +- add 8213397-Stack-dump-should-show-more-clearly-when-a-t.patch +- add Record-the-number-of-processes-to-errlog-file.patch.patch +- add 8254723-Add-diagnostic-command-to-write-Linux-perf-m.patch +- add The-OverWriteOldestGCLog-option-is-added-to-control.patch +- add add-6899049-G1-Clean-up-code-in-ptrQueue.-ch-pp-and-.patch +- add add-make-Appcds-jsa-rw-region-deterministic.patch +- add add-8142508-To-bring-j.u.z.ZipFile-s-native-implemen.patch +- add add-8198423-Improve-metaspace-chunk-allocation.patch +- add add-8226530-ZipFile-reads-wrong-entry-size-from-ZIP6.patch +- add add-fix-lock_fd-no-close-and-improve-KAEProvider.patch +- add add-8242842-Avoid-reallocating-name-when-checking-fo.patch +- add add-8170831-ZipFile-implementation-no-longer-caches-.patch +- add add-8146431-j.u.z.ZipFile.getEntry-throws-AIOOBE.patch +- add add-8226530-test-case-fixed.patch +- add add-8147940-Modify-testcase-this-test-does-not-assum.patch +- add add-8191924-Adjust-DelegatingClassLoader-s-metadata-.patch +- add add-Do-not-collect_class-when-DynamicCDS-dump.patch +- add add-add-Count-instance-klass-when-loading-from-jsa-f.patch +- add add-Adapting-IOException-of-Zip-to-ZipException.patch +- add add-8227041-runtime-memory-RunUnitTestsConcurrently.patch +- add add-fix-windows-build-Dynamic-CDS-failure.patch +- add add-Fix-aarch64-runtime-thread-signal-transfer-bug.patch +- add 8295068-SSLEngine-throws-NPE-parsing-CertificateRequ.patch + +* Mon Sep 11 2023 kuenking111 - 1:1.8.0.382-b05.8 +- Fix openjfx bingding issues + +* Wed Sep 6 2023 kuenking111 - 1:1.8.0.382-b05.7 - del 0013-8287349-AArch64-Merge-LDR-instructions-to-improve-C1.patch -* Wed Aug 30 2023 kuenking111 - 1:1.8.0.382-b05.4 +* Fri Sep 1 2023 noah - 1:1.8.0.382-b05.6 +- removed cjc backward comaptiblity, to fix when both rpm 4.16 and 4.17 are in transaction + +* Wed Aug 30 2023 kuenking111 - 1:1.8.0.382-b05.5 - del 0022-8198510-Enable-UseDynamicNumberOfGCThreads-by-defaul.patch -* Fri Aug 25 2023 kuenking111 - 1:1.8.0.382-b05.3 +* Fri Aug 25 2023 kuenking111 - 1:1.8.0.382-b05.4 - add 8283441-C2-segmentation-fault-in-ciMethodBlocks-make.patch - fix cve-2022-40433 +* Thu Aug 24 2023 misaka00251 - 1:1.8.0.382-b05.3 +- Init riscv64 support + * Thu Aug 24 2023 kuenking111 - 1:1.8.0.382-b05.2 - deleted 0053-8146987-Improve-Parallel-GC-Full-GC-by-caching-resul.patch -- Gitee