Merge branch 'openjdk:master' into cas-alloc-1

This commit is contained in:
Xiaolong Peng 2026-01-13 12:05:45 -08:00 committed by GitHub
commit 9e0520ba48
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
246 changed files with 12084 additions and 3005 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2026, 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
@ -6086,7 +6086,7 @@ void MacroAssembler::generate_fill(BasicType t, bool aligned,
vpbroadcastd(xtmp, xtmp, Assembler::AVX_512bit);
subptr(count, 16 << shift);
jccb(Assembler::less, L_check_fill_32_bytes);
jcc(Assembler::less, L_check_fill_32_bytes);
align(16);
BIND(L_fill_64_bytes_loop_avx3);

View File

@ -458,12 +458,10 @@ char* os::map_memory_to_file(char* base, size_t size, int fd) {
warning("Failed mmap to file. (%s)", os::strerror(errno));
return nullptr;
}
if (base != nullptr && addr != base) {
if (!os::release_memory(addr, size)) {
warning("Could not release memory on unsuccessful file mapping");
}
return nullptr;
}
// The requested address should be the same as the returned address when using MAP_FIXED
// as per POSIX.
assert(base == nullptr || addr == base, "base should equal addr when using MAP_FIXED");
return addr;
}

View File

@ -1,6 +1,6 @@
/*
* Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2021 SAP SE. All rights reserved.
* Copyright (c) 2001, 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2026 SAP SE. 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
@ -946,7 +946,7 @@ static int create_sharedmem_file(const char* dirname, const char* filename, size
if (result == -1 ) break;
if (!os::write(fd, &zero_int, 1)) {
if (errno == ENOSPC) {
warning("Insufficient space for shared memory file:\n %s\nTry using the -Djava.io.tmpdir= option to select an alternate temp location.\n", filename);
warning("Insufficient space for shared memory file: %s/%s\n", dirname, filename);
}
result = OS_ERR;
break;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2026, 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
@ -951,6 +951,32 @@ struct enum_sigcode_desc_t {
const char* s_desc;
};
#if defined(LINUX)
// Additional kernel si_code definitions that are only exported by
// more recent glibc distributions, so we have to hard-code the values.
#ifndef BUS_MCEERR_AR // glibc 2.17
#define BUS_MCEERR_AR 4
#define BUS_MCEERR_AO 5
#endif
#ifndef SEGV_PKUERR // glibc 2.27
#define SEGV_PKUERR 4
#endif
#ifndef SYS_SECCOMP // glibc 2.28
#define SYS_SECCOMP 1
#endif
#ifndef TRAP_BRANCH // glibc 2.30
#define TRAP_BRANCH 3
#endif
#ifndef TRAP_HWBKPT // not glibc version specific - gdb related
#define TRAP_HWBKPT 4
#endif
#endif // LINUX
static bool get_signal_code_description(const siginfo_t* si, enum_sigcode_desc_t* out) {
const struct {
@ -976,6 +1002,7 @@ static bool get_signal_code_description(const siginfo_t* si, enum_sigcode_desc_t
{ SIGSEGV, SEGV_ACCERR, "SEGV_ACCERR", "Invalid permissions for mapped object." },
#if defined(LINUX)
{ SIGSEGV, SEGV_BNDERR, "SEGV_BNDERR", "Failed address bound checks." },
{ SIGSEGV, SEGV_PKUERR, "SEGV_PKUERR", "Protection key checking failure." },
#endif
#if defined(AIX)
// no explanation found what keyerr would be
@ -984,8 +1011,18 @@ static bool get_signal_code_description(const siginfo_t* si, enum_sigcode_desc_t
{ SIGBUS, BUS_ADRALN, "BUS_ADRALN", "Invalid address alignment." },
{ SIGBUS, BUS_ADRERR, "BUS_ADRERR", "Nonexistent physical address." },
{ SIGBUS, BUS_OBJERR, "BUS_OBJERR", "Object-specific hardware error." },
#if defined(LINUX)
{ SIGBUS, BUS_MCEERR_AR,"BUS_MCEERR_AR","Hardware memory error consumed on a machine check: action required." },
{ SIGBUS, BUS_MCEERR_AO,"BUS_MCEERR_AO","Hardware memory error detected in process but not consumed: action optional." },
{ SIGSYS, SYS_SECCOMP, "SYS_SECCOMP", "Secure computing (seccomp) filter failure." },
#endif
{ SIGTRAP, TRAP_BRKPT, "TRAP_BRKPT", "Process breakpoint." },
{ SIGTRAP, TRAP_TRACE, "TRAP_TRACE", "Process trace trap." },
#if defined(LINUX)
{ SIGTRAP, TRAP_BRANCH, "TRAP_BRANCH", "Process taken branch trap." },
{ SIGTRAP, TRAP_HWBKPT, "TRAP_HWBKPT", "Hardware breakpoint/watchpoint." },
#endif
{ SIGCHLD, CLD_EXITED, "CLD_EXITED", "Child has exited." },
{ SIGCHLD, CLD_KILLED, "CLD_KILLED", "Child has terminated abnormally and did not create a core file." },
{ SIGCHLD, CLD_DUMPED, "CLD_DUMPED", "Child has terminated abnormally and created a core file." },
@ -993,6 +1030,7 @@ static bool get_signal_code_description(const siginfo_t* si, enum_sigcode_desc_t
{ SIGCHLD, CLD_STOPPED, "CLD_STOPPED", "Child has stopped." },
{ SIGCHLD, CLD_CONTINUED,"CLD_CONTINUED","Stopped child has continued." },
#ifdef SIGPOLL
{ SIGPOLL, POLL_IN, "POLL_IN", "Data input available." },
{ SIGPOLL, POLL_OUT, "POLL_OUT", "Output buffers available." },
{ SIGPOLL, POLL_MSG, "POLL_MSG", "Input message available." },
{ SIGPOLL, POLL_ERR, "POLL_ERR", "I/O error." },

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2013 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@ -26,8 +26,7 @@
#ifndef OS_CPU_AIX_PPC_PREFETCH_AIX_PPC_INLINE_HPP
#define OS_CPU_AIX_PPC_PREFETCH_AIX_PPC_INLINE_HPP
#include "runtime/prefetch.hpp"
// Included in runtime/prefetch.inline.hpp
inline void Prefetch::read(const void *loc, intx interval) {
#if !defined(USE_XLC_BUILTINS)

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, Red Hat Inc. All rights reserved.
* Copyright (c) 2021, Azul Systems, Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
@ -27,8 +27,7 @@
#ifndef OS_CPU_BSD_AARCH64_PREFETCH_BSD_AARCH64_INLINE_HPP
#define OS_CPU_BSD_AARCH64_PREFETCH_BSD_AARCH64_INLINE_HPP
#include "runtime/prefetch.hpp"
// Included in runtime/prefetch.inline.hpp
inline void Prefetch::read (const void *loc, intx interval) {
if (interval >= 0)

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2026, 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
@ -25,8 +25,7 @@
#ifndef OS_CPU_BSD_X86_PREFETCH_BSD_X86_INLINE_HPP
#define OS_CPU_BSD_X86_PREFETCH_BSD_X86_INLINE_HPP
#include "runtime/prefetch.hpp"
// Included in runtime/prefetch.inline.hpp
inline void Prefetch::read (const void *loc, intx interval) {
__asm__ ("prefetcht0 (%0,%1,1)" : : "r" (loc), "r" (interval));

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright 2007, 2008 Red Hat, Inc.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@ -26,7 +26,7 @@
#ifndef OS_CPU_BSD_ZERO_PREFETCH_BSD_ZERO_INLINE_HPP
#define OS_CPU_BSD_ZERO_PREFETCH_BSD_ZERO_INLINE_HPP
#include "runtime/prefetch.hpp"
// Included in runtime/prefetch.inline.hpp
inline void Prefetch::read(const void* loc, intx interval) {
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, Red Hat Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@ -26,8 +26,7 @@
#ifndef OS_CPU_LINUX_AARCH64_PREFETCH_LINUX_AARCH64_INLINE_HPP
#define OS_CPU_LINUX_AARCH64_PREFETCH_LINUX_AARCH64_INLINE_HPP
#include "runtime/prefetch.hpp"
// Included in runtime/prefetch.inline.hpp
inline void Prefetch::read (const void *loc, intx interval) {
if (interval >= 0)

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2008, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2008, 2026, 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
@ -25,7 +25,7 @@
#ifndef OS_CPU_LINUX_ARM_PREFETCH_LINUX_ARM_INLINE_HPP
#define OS_CPU_LINUX_ARM_PREFETCH_LINUX_ARM_INLINE_HPP
#include "runtime/prefetch.hpp"
// Included in runtime/prefetch.inline.hpp
inline void Prefetch::read (const void *loc, intx interval) {
#if defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_5TE__)

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2013 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@ -26,8 +26,7 @@
#ifndef OS_CPU_LINUX_PPC_PREFETCH_LINUX_PPC_INLINE_HPP
#define OS_CPU_LINUX_PPC_PREFETCH_LINUX_PPC_INLINE_HPP
#include "runtime/prefetch.hpp"
// Included in runtime/prefetch.inline.hpp
inline void Prefetch::read(const void *loc, intx interval) {
__asm__ __volatile__ (

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2021, Huawei Technologies Co., Ltd. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@ -26,7 +26,7 @@
#ifndef OS_CPU_LINUX_RISCV_VM_PREFETCH_LINUX_RISCV_INLINE_HPP
#define OS_CPU_LINUX_RISCV_VM_PREFETCH_LINUX_RISCV_INLINE_HPP
#include "runtime/prefetch.hpp"
// Included in runtime/prefetch.inline.hpp
inline void Prefetch::read (const void *loc, intx interval) {
if (interval >= 0 && UseZicbop) {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@ -26,7 +26,7 @@
#ifndef OS_CPU_LINUX_S390_PREFETCH_LINUX_S390_INLINE_HPP
#define OS_CPU_LINUX_S390_PREFETCH_LINUX_S390_INLINE_HPP
#include "runtime/prefetch.hpp"
// Included in runtime/prefetch.inline.hpp
inline void Prefetch::read(const void* loc, intx interval) {
// No prefetch instructions on z/Architecture -> implement trivially.

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2026, 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
@ -25,8 +25,7 @@
#ifndef OS_CPU_LINUX_X86_PREFETCH_LINUX_X86_INLINE_HPP
#define OS_CPU_LINUX_X86_PREFETCH_LINUX_X86_INLINE_HPP
#include "runtime/prefetch.hpp"
// Included in runtime/prefetch.inline.hpp
inline void Prefetch::read (const void *loc, intx interval) {
__asm__ ("prefetcht0 (%0,%1,1)" : : "r" (loc), "r" (interval));

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright 2007, 2008 Red Hat, Inc.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@ -26,7 +26,7 @@
#ifndef OS_CPU_LINUX_ZERO_PREFETCH_LINUX_ZERO_INLINE_HPP
#define OS_CPU_LINUX_ZERO_PREFETCH_LINUX_ZERO_INLINE_HPP
#include "runtime/prefetch.hpp"
// Included in runtime/prefetch.inline.hpp
inline void Prefetch::read(const void* loc, intx interval) {
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, Microsoft Corporation. All rights reserved.
* Copyright (c) 2020, 2026, Microsoft Corporation. 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
@ -25,8 +25,7 @@
#ifndef OS_CPU_WINDOWS_AARCH64_PREFETCH_WINDOWS_AARCH64_INLINE_HPP
#define OS_CPU_WINDOWS_AARCH64_PREFETCH_WINDOWS_AARCH64_INLINE_HPP
#include "runtime/prefetch.hpp"
// Included in runtime/prefetch.inline.hpp
inline void Prefetch::read (const void *loc, intx interval) {
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2026, 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
@ -25,7 +25,7 @@
#ifndef OS_CPU_WINDOWS_X86_PREFETCH_WINDOWS_X86_INLINE_HPP
#define OS_CPU_WINDOWS_X86_PREFETCH_WINDOWS_X86_INLINE_HPP
#include "runtime/prefetch.hpp"
// Included in runtime/prefetch.inline.hpp
inline void Prefetch::read (const void *loc, intx interval) {}
inline void Prefetch::write(void *loc, intx interval) {}

View File

@ -62,8 +62,6 @@ jint EpsilonHeap::initialize() {
// Enable monitoring
_monitoring_support = new EpsilonMonitoringSupport(this);
_last_counter_update = 0;
_last_heap_print = 0;
// Install barrier set
BarrierSet::set_barrier_set(new EpsilonBarrierSet());
@ -77,6 +75,7 @@ jint EpsilonHeap::initialize() {
void EpsilonHeap::initialize_serviceability() {
_pool = new EpsilonMemoryPool(this);
_memory_manager.add_pool(_pool);
_monitoring_support->mark_ready();
}
GrowableArray<GCMemoryManager*> EpsilonHeap::memory_managers() {
@ -101,7 +100,7 @@ EpsilonHeap* EpsilonHeap::heap() {
return named_heap<EpsilonHeap>(CollectedHeap::Epsilon);
}
HeapWord* EpsilonHeap::allocate_work(size_t size, bool verbose) {
HeapWord* EpsilonHeap::allocate_work(size_t size) {
assert(is_object_aligned(size), "Allocation size should be aligned: %zu", size);
HeapWord* res = nullptr;
@ -151,19 +150,23 @@ HeapWord* EpsilonHeap::allocate_work(size_t size, bool verbose) {
size_t used = _space->used();
// Allocation successful, update counters
if (verbose) {
size_t last = _last_counter_update;
if ((used - last >= _step_counter_update) && AtomicAccess::cmpxchg(&_last_counter_update, last, used) == last) {
// Allocation successful, update counters and print status.
// At this point, some diagnostic subsystems might not yet be initialized.
// We pretend the printout happened either way. This keeps allocation path
// from obsessively checking the subsystems' status on every allocation.
size_t last_counter = _last_counter_update.load_relaxed();
if ((used - last_counter >= _step_counter_update) &&
_last_counter_update.compare_set(last_counter, used)) {
if (_monitoring_support->is_ready()) {
_monitoring_support->update_counters();
}
}
// ...and print the occupancy line, if needed
if (verbose) {
size_t last = _last_heap_print;
if ((used - last >= _step_heap_print) && AtomicAccess::cmpxchg(&_last_heap_print, last, used) == last) {
print_heap_info(used);
size_t last_heap = _last_heap_print.load_relaxed();
if ((used - last_heap >= _step_heap_print) &&
_last_heap_print.compare_set(last_heap, used)) {
print_heap_info(used);
if (Metaspace::initialized()) {
print_metaspace_info();
}
}
@ -265,8 +268,7 @@ HeapWord* EpsilonHeap::mem_allocate(size_t size) {
}
HeapWord* EpsilonHeap::allocate_loaded_archive_space(size_t size) {
// Cannot use verbose=true because Metaspace is not initialized
return allocate_work(size, /* verbose = */false);
return allocate_work(size);
}
void EpsilonHeap::collect(GCCause::Cause cause) {

View File

@ -31,6 +31,7 @@
#include "gc/shared/collectedHeap.hpp"
#include "gc/shared/space.hpp"
#include "memory/virtualspace.hpp"
#include "runtime/atomic.hpp"
#include "services/memoryManager.hpp"
class EpsilonHeap : public CollectedHeap {
@ -45,8 +46,8 @@ private:
size_t _step_counter_update;
size_t _step_heap_print;
int64_t _decay_time_ns;
volatile size_t _last_counter_update;
volatile size_t _last_heap_print;
Atomic<size_t> _last_counter_update;
Atomic<size_t> _last_heap_print;
void print_tracing_info() const override;
void stop() override {};
@ -83,7 +84,7 @@ public:
bool requires_barriers(stackChunkOop obj) const override { return false; }
// Allocation
HeapWord* allocate_work(size_t size, bool verbose = true);
HeapWord* allocate_work(size_t size);
HeapWord* mem_allocate(size_t size) override;
HeapWord* allocate_new_tlab(size_t min_size,
size_t requested_size,

View File

@ -99,6 +99,7 @@ EpsilonMonitoringSupport::EpsilonMonitoringSupport(EpsilonHeap* heap) {
}
void EpsilonMonitoringSupport::update_counters() {
assert(is_ready(), "Must be ready");
MemoryService::track_memory_usage();
if (UsePerfData) {
@ -110,3 +111,11 @@ void EpsilonMonitoringSupport::update_counters() {
MetaspaceCounters::update_performance_counters();
}
}
bool EpsilonMonitoringSupport::is_ready() {
return _ready.load_acquire();
}
void EpsilonMonitoringSupport::mark_ready() {
_ready.release_store(true);
}

View File

@ -26,6 +26,7 @@
#define SHARE_GC_EPSILON_EPSILONMONITORINGSUPPORT_HPP
#include "memory/allocation.hpp"
#include "runtime/atomic.hpp"
class EpsilonGenerationCounters;
class EpsilonSpaceCounters;
@ -35,9 +36,12 @@ class EpsilonMonitoringSupport : public CHeapObj<mtGC> {
private:
EpsilonGenerationCounters* _heap_counters;
EpsilonSpaceCounters* _space_counters;
Atomic<bool> _ready;
public:
EpsilonMonitoringSupport(EpsilonHeap* heap);
bool is_ready();
void mark_ready();
void update_counters();
};

View File

@ -36,6 +36,7 @@
#include "gc/shared/fullGCForwarding.hpp"
#include "gc/shared/gcArguments.hpp"
#include "gc/shared/workerPolicy.hpp"
#include "runtime/flags/jvmFlagLimit.hpp"
#include "runtime/globals.hpp"
#include "runtime/globals_extension.hpp"
#include "runtime/java.hpp"
@ -190,7 +191,8 @@ void G1Arguments::initialize() {
}
FLAG_SET_DEFAULT(G1ConcRefinementThreads, 0);
} else if (FLAG_IS_DEFAULT(G1ConcRefinementThreads)) {
FLAG_SET_ERGO(G1ConcRefinementThreads, ParallelGCThreads);
const JVMTypedFlagLimit<uint>* conc_refinement_threads_limits = JVMFlagLimit::get_range_at(FLAG_MEMBER_ENUM(G1ConcRefinementThreads))->cast<uint>();
FLAG_SET_ERGO(G1ConcRefinementThreads, MIN2(ParallelGCThreads, conc_refinement_threads_limits->max()));
}
if (FLAG_IS_DEFAULT(ConcGCThreads) || ConcGCThreads == 0) {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2001, 2026, 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
@ -686,7 +686,8 @@ HeapWord* G1CollectedHeap::attempt_allocation_humongous(size_t word_size) {
// the check before we do the actual allocation. The reason for doing it
// before the allocation is that we avoid having to keep track of the newly
// allocated memory while we do a GC.
if (policy()->need_to_start_conc_mark("concurrent humongous allocation",
// Only try that if we can actually perform a GC.
if (is_init_completed() && policy()->need_to_start_conc_mark("concurrent humongous allocation",
word_size)) {
try_collect(word_size, GCCause::_g1_humongous_allocation, collection_counters(this));
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, 2026, 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
@ -46,7 +46,7 @@
#include "oops/access.inline.hpp"
#include "oops/compressedOops.inline.hpp"
#include "oops/oop.inline.hpp"
#include "runtime/prefetch.hpp"
#include "runtime/prefetch.inline.hpp"
#include "runtime/threads.hpp"
#include "runtime/threadSMR.hpp"
#include "utilities/bitMap.inline.hpp"

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2001, 2026, 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
@ -28,6 +28,7 @@
#include "gc/serial/serialHeap.inline.hpp"
#include "gc/shared/space.hpp"
#include "memory/iterator.inline.hpp"
#include "runtime/prefetch.inline.hpp"
#include "utilities/align.hpp"
void CardTableRS::scan_old_to_young_refs(TenuredGeneration* tg, HeapWord* saved_top) {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2026, 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
@ -34,7 +34,6 @@
#include "memory/virtualspace.hpp"
#include "runtime/mutex.hpp"
#include "runtime/perfData.hpp"
#include "runtime/prefetch.inline.hpp"
// A Generation models a heap area for similarly-aged objects.
// It will contain one ore more spaces holding the actual objects.

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2017, 2026, 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
@ -70,6 +70,7 @@
#include "runtime/init.hpp"
#include "runtime/java.hpp"
#include "runtime/mutexLocker.hpp"
#include "runtime/prefetch.inline.hpp"
#include "runtime/threads.hpp"
#include "runtime/vmThread.hpp"
#include "services/memoryManager.hpp"

View File

@ -700,7 +700,7 @@ void OopStorage::Block::release_entries(uintx releasing, OopStorage* owner) {
// then someone else has made such a claim and the deferred update has not
// yet been processed and will include our change, so we don't need to do
// anything further.
if (_deferred_updates_next.compare_exchange(nullptr, this) == nullptr) {
if (_deferred_updates_next.compare_set(nullptr, this)) {
// Successfully claimed. Push, with self-loop for end-of-list.
Block* head = owner->_deferred_updates.load_relaxed();
while (true) {

View File

@ -56,7 +56,7 @@ void PretouchTask::work(uint worker_id) {
char* cur_end = cur_start + MIN2(_chunk_size, pointer_delta(_end_addr, cur_start, 1));
if (cur_start >= cur_end) {
break;
} else if (cur_start == _cur_addr.compare_exchange(cur_start, cur_end)) {
} else if (_cur_addr.compare_set(cur_start, cur_end)) {
os::pretouch_memory(cur_start, cur_end, _page_size);
} // Else attempt to claim chunk failed, so try again.
}

View File

@ -183,8 +183,8 @@ protected:
_age.store_relaxed(new_age);
}
Age cmpxchg_age(Age old_age, Age new_age) {
return _age.compare_exchange(old_age, new_age);
bool par_set_age(Age old_age, Age new_age) {
return _age.compare_set(old_age, new_age);
}
idx_t age_top_relaxed() const {
@ -345,7 +345,7 @@ protected:
using TaskQueueSuper<N, MT>::age_relaxed;
using TaskQueueSuper<N, MT>::set_age_relaxed;
using TaskQueueSuper<N, MT>::cmpxchg_age;
using TaskQueueSuper<N, MT>::par_set_age;
using TaskQueueSuper<N, MT>::age_top_relaxed;
using TaskQueueSuper<N, MT>::increment_index;

View File

@ -170,8 +170,7 @@ bool GenericTaskQueue<E, MT, N>::pop_local_slow(uint localBot, Age oldAge) {
if (localBot == oldAge.top()) {
// No competing pop_global has yet incremented "top"; we'll try to
// install new_age, thus claiming the element.
Age tempAge = cmpxchg_age(oldAge, newAge);
if (tempAge == oldAge) {
if (par_set_age(oldAge, newAge)) {
// We win.
assert_not_underflow(localBot, age_top_relaxed());
TASKQUEUE_STATS_ONLY(stats.record_pop_slow());
@ -283,12 +282,12 @@ typename GenericTaskQueue<E, MT, N>::PopResult GenericTaskQueue<E, MT, N>::pop_g
idx_t new_top = increment_index(oldAge.top());
idx_t new_tag = oldAge.tag() + ((new_top == 0) ? 1 : 0);
Age newAge(new_top, new_tag);
Age resAge = cmpxchg_age(oldAge, newAge);
bool result = par_set_age(oldAge, newAge);
// Note that using "bottom" here might fail, since a pop_local might
// have decremented it.
assert_not_underflow(localBot, newAge.top());
return resAge == oldAge ? PopResult::Success : PopResult::Contended;
return result ? PopResult::Success : PopResult::Contended;
}
inline int randomParkAndMiller(int *seed0) {

View File

@ -61,7 +61,12 @@ ShenandoahGenerationalControlThread::ShenandoahGenerationalControlThread() :
void ShenandoahGenerationalControlThread::run_service() {
// This is the only instance of request. It is important that request.generation
// does not change between a concurrent cycle failure and the start of a degenerated
// cycle. We initialize it with the young generation to handle the pathological case
// where the very first cycle is degenerated (some tests exercise this path).
ShenandoahGCRequest request;
request.generation = _heap->young_generation();
while (!should_terminate()) {
// Figure out if we have pending requests.
@ -77,12 +82,10 @@ void ShenandoahGenerationalControlThread::run_service() {
// If the cycle was cancelled, continue the next iteration to deal with it. Otherwise,
// if there was no other cycle requested, cleanup and wait for the next request.
if (!_heap->cancelled_gc()) {
MonitorLocker ml(&_control_lock, Mutex::_no_safepoint_check_flag);
if (_requested_gc_cause == GCCause::_no_gc) {
set_gc_mode(ml, none);
ml.wait();
}
MonitorLocker ml(&_control_lock, Mutex::_no_safepoint_check_flag);
if (_requested_gc_cause == GCCause::_no_gc) {
set_gc_mode(ml, none);
ml.wait();
}
}
@ -96,8 +99,7 @@ void ShenandoahGenerationalControlThread::stop_service() {
log_debug(gc, thread)("Stopping control thread");
MonitorLocker ml(&_control_lock, Mutex::_no_safepoint_check_flag);
_heap->cancel_gc(GCCause::_shenandoah_stop_vm);
_requested_gc_cause = GCCause::_shenandoah_stop_vm;
notify_cancellation(ml, GCCause::_shenandoah_stop_vm);
notify_control_thread(ml, GCCause::_shenandoah_stop_vm);
// We can't wait here because it may interfere with the active cycle's ability
// to reach a safepoint (this runs on a java thread).
}
@ -105,29 +107,39 @@ void ShenandoahGenerationalControlThread::stop_service() {
void ShenandoahGenerationalControlThread::check_for_request(ShenandoahGCRequest& request) {
// Hold the lock while we read request cause and generation
MonitorLocker ml(&_control_lock, Mutex::_no_safepoint_check_flag);
if (_heap->cancelled_gc()) {
// The previous request was cancelled. Either it was cancelled for an allocation
// failure (degenerated cycle), or old marking was cancelled to run a young collection.
// In either case, the correct generation for the next cycle can be determined by
// the cancellation cause.
request.cause = _heap->clear_cancellation(GCCause::_shenandoah_concurrent_gc);
if (request.cause == GCCause::_shenandoah_concurrent_gc) {
log_debug(gc, thread)("cancelled cause: %s, requested cause: %s",
GCCause::to_string(_heap->cancelled_cause()), GCCause::to_string(_requested_gc_cause));
request.cause = _requested_gc_cause;
if (ShenandoahCollectorPolicy::is_allocation_failure(request.cause)) {
if (_degen_point == ShenandoahGC::_degenerated_unset) {
request.generation = _heap->young_generation();
_degen_point = ShenandoahGC::_degenerated_outside_cycle;
} else {
assert(request.generation != nullptr, "Must know which generation to use for degenerated cycle");
}
} else {
request.cause = _requested_gc_cause;
if (request.cause == GCCause::_shenandoah_concurrent_gc) {
// This is a regulator request. It is also possible that the regulator "canceled" an old mark,
// so we can clear that here. This clear operation will only clear the cancellation if it is
// a regulator request.
_heap->clear_cancellation(GCCause::_shenandoah_concurrent_gc);
}
request.generation = _requested_generation;
// Only clear these if we made a request from them. In the case of a cancelled gc,
// we do not want to inadvertently lose this pending request.
_requested_gc_cause = GCCause::_no_gc;
_requested_generation = nullptr;
}
log_debug(gc, thread)("request.cause: %s, request.generation: %s",
GCCause::to_string(request.cause), request.generation == nullptr ? "None" : request.generation->name());
_requested_gc_cause = GCCause::_no_gc;
_requested_generation = nullptr;
if (request.cause == GCCause::_no_gc || request.cause == GCCause::_shenandoah_stop_vm) {
return;
}
assert(request.generation != nullptr, "request.generation cannot be null, cause is: %s", GCCause::to_string(request.cause));
GCMode mode;
if (ShenandoahCollectorPolicy::is_allocation_failure(request.cause)) {
mode = prepare_for_allocation_failure_gc(request);
@ -140,11 +152,9 @@ void ShenandoahGenerationalControlThread::check_for_request(ShenandoahGCRequest&
}
ShenandoahGenerationalControlThread::GCMode ShenandoahGenerationalControlThread::prepare_for_allocation_failure_gc(ShenandoahGCRequest &request) {
if (_degen_point == ShenandoahGC::_degenerated_unset) {
_degen_point = ShenandoahGC::_degenerated_outside_cycle;
request.generation = _heap->young_generation();
} else if (request.generation->is_old()) {
// Important: not all paths update the request.generation. This is intentional.
// A degenerated cycle must use the same generation carried over from the previous request.
if (request.generation->is_old()) {
// This means we degenerated during the young bootstrap for the old generation
// cycle. The following degenerated cycle should therefore also be young.
request.generation = _heap->young_generation();
@ -588,6 +598,8 @@ bool ShenandoahGenerationalControlThread::check_cancellation_or_degen(Shenandoah
if (ShenandoahCollectorPolicy::is_allocation_failure(_heap->cancelled_cause())) {
assert(_degen_point == ShenandoahGC::_degenerated_unset,
"Should not be set yet: %s", ShenandoahGC::degen_point_to_string(_degen_point));
MonitorLocker ml(&_control_lock, Mutex::_no_safepoint_check_flag);
_requested_gc_cause = _heap->cancelled_cause();
_degen_point = point;
log_debug(gc, thread)("Cancellation detected:, reason: %s, degen point: %s",
GCCause::to_string(_heap->cancelled_cause()),
@ -633,9 +645,7 @@ void ShenandoahGenerationalControlThread::service_stw_degenerated_cycle(const Sh
void ShenandoahGenerationalControlThread::request_gc(GCCause::Cause cause) {
if (ShenandoahCollectorPolicy::is_allocation_failure(cause)) {
// GC should already be cancelled. Here we are just notifying the control thread to
// wake up and handle the cancellation request, so we don't need to set _requested_gc_cause.
notify_cancellation(cause);
notify_control_thread(cause);
} else if (ShenandoahCollectorPolicy::should_handle_requested_gc(cause)) {
handle_requested_gc(cause);
}
@ -653,7 +663,8 @@ bool ShenandoahGenerationalControlThread::request_concurrent_gc(ShenandoahGenera
MonitorLocker ml(&_control_lock, Mutex::_no_safepoint_check_flag);
if (gc_mode() == servicing_old) {
if (!preempt_old_marking(generation)) {
log_debug(gc, thread)("Cannot start young, old collection is not preemptible");
// Global should be able to cause old collection to be abandoned
log_debug(gc, thread)("Cannot start %s, old collection is not preemptible", generation->name());
return false;
}
@ -661,7 +672,7 @@ bool ShenandoahGenerationalControlThread::request_concurrent_gc(ShenandoahGenera
log_info(gc)("Preempting old generation mark to allow %s GC", generation->name());
while (gc_mode() == servicing_old) {
ShenandoahHeap::heap()->cancel_gc(GCCause::_shenandoah_concurrent_gc);
notify_cancellation(ml, GCCause::_shenandoah_concurrent_gc);
notify_control_thread(ml, GCCause::_shenandoah_concurrent_gc, generation);
ml.wait();
}
return true;
@ -695,21 +706,34 @@ void ShenandoahGenerationalControlThread::notify_control_thread(GCCause::Cause c
void ShenandoahGenerationalControlThread::notify_control_thread(MonitorLocker& ml, GCCause::Cause cause, ShenandoahGeneration* generation) {
assert(_control_lock.is_locked(), "Request lock must be held here");
log_debug(gc, thread)("Notify control (%s): %s, %s", gc_mode_name(gc_mode()), GCCause::to_string(cause), generation->name());
_requested_gc_cause = cause;
_requested_generation = generation;
ml.notify();
if (ShenandoahCollectorPolicy::is_allocation_failure(_requested_gc_cause)) {
// We have already observed a request to handle an allocation failure. We cannot allow
// another request (System.gc or regulator) to subvert the degenerated cycle.
log_debug(gc, thread)("Not overwriting gc cause %s with %s", GCCause::to_string(_requested_gc_cause), GCCause::to_string(cause));
} else {
log_debug(gc, thread)("Notify control (%s): %s, %s", gc_mode_name(gc_mode()), GCCause::to_string(cause), generation->name());
_requested_gc_cause = cause;
_requested_generation = generation;
ml.notify();
}
}
void ShenandoahGenerationalControlThread::notify_cancellation(GCCause::Cause cause) {
void ShenandoahGenerationalControlThread::notify_control_thread(GCCause::Cause cause) {
MonitorLocker ml(&_control_lock, Mutex::_no_safepoint_check_flag);
notify_cancellation(ml, cause);
notify_control_thread(ml, cause);
}
void ShenandoahGenerationalControlThread::notify_cancellation(MonitorLocker& ml, GCCause::Cause cause) {
assert(_heap->cancelled_gc(), "GC should already be cancelled");
log_debug(gc,thread)("Notify control (%s): %s", gc_mode_name(gc_mode()), GCCause::to_string(cause));
ml.notify();
void ShenandoahGenerationalControlThread::notify_control_thread(MonitorLocker& ml, GCCause::Cause cause) {
assert(_control_lock.is_locked(), "Request lock must be held here");
if (ShenandoahCollectorPolicy::is_allocation_failure(_requested_gc_cause)) {
// We have already observed a request to handle an allocation failure. We cannot allow
// another request (System.gc or regulator) to subvert the degenerated cycle.
log_debug(gc, thread)("Not overwriting gc cause %s with %s", GCCause::to_string(_requested_gc_cause), GCCause::to_string(cause));
} else {
log_debug(gc, thread)("Notify control (%s): %s", gc_mode_name(gc_mode()), GCCause::to_string(cause));
_requested_gc_cause = cause;
ml.notify();
}
}
bool ShenandoahGenerationalControlThread::preempt_old_marking(ShenandoahGeneration* generation) {

View File

@ -135,16 +135,13 @@ private:
// Return printable name for the given gc mode.
static const char* gc_mode_name(GCMode mode);
// Takes the request lock and updates the requested cause and generation, then notifies the control thread.
// The overloaded variant should be used when the _control_lock is already held.
// These notify the control thread after updating _requested_gc_cause and (optionally) _requested_generation.
// Updating the requested generation is not necessary for allocation failures nor when stopping the thread.
void notify_control_thread(GCCause::Cause cause);
void notify_control_thread(MonitorLocker& ml, GCCause::Cause cause);
void notify_control_thread(GCCause::Cause cause, ShenandoahGeneration* generation);
void notify_control_thread(MonitorLocker& ml, GCCause::Cause cause, ShenandoahGeneration* generation);
// Notifies the control thread, but does not update the requested cause or generation.
// The overloaded variant should be used when the _control_lock is already held.
void notify_cancellation(GCCause::Cause cause);
void notify_cancellation(MonitorLocker& ml, GCCause::Cause cause);
// Configure the heap to age objects and regions if the aging period has elapsed.
void maybe_set_aging_cycle();

View File

@ -44,13 +44,13 @@ public:
void post_initialize_heuristics() override;
static ShenandoahGenerationalHeap* heap() {
assert(ShenandoahCardBarrier, "Should have card barrier to use genenrational heap");
assert(ShenandoahCardBarrier, "Should have card barrier to use generational heap");
CollectedHeap* heap = Universe::heap();
return cast(heap);
}
static ShenandoahGenerationalHeap* cast(CollectedHeap* heap) {
assert(ShenandoahCardBarrier, "Should have card barrier to use genenrational heap");
assert(ShenandoahCardBarrier, "Should have card barrier to use generational heap");
return checked_cast<ShenandoahGenerationalHeap*>(heap);
}

View File

@ -149,6 +149,13 @@ bool ShenandoahRegulatorThread::start_global_cycle() const {
bool ShenandoahRegulatorThread::request_concurrent_gc(ShenandoahGeneration* generation) const {
double now = os::elapsedTime();
// This call may find the control thread waiting on workers which have suspended
// to allow a safepoint to run. If this regulator thread does not yield, the safepoint
// will not run. The worker threads won't progress, the control thread won't progress,
// and the regulator thread may never yield. Therefore, we leave the suspendible
// thread set before making this call.
SuspendibleThreadSetLeaver leaver;
bool accepted = _control_thread->request_concurrent_gc(generation);
if (LogTarget(Debug, gc, thread)::is_enabled() && accepted) {
double wait_time = os::elapsedTime() - now;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 2026, 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
@ -32,6 +32,7 @@
#include "jfr/recorder/repository/jfrEmergencyDump.hpp"
#include "jfr/recorder/repository/jfrRepository.hpp"
#include "jfr/recorder/service/jfrOptionSet.hpp"
#include "jfr/recorder/service/jfrRecorderService.hpp"
#include "jfr/support/jfrClassDefineEvent.hpp"
#include "jfr/support/jfrKlassExtension.hpp"
#include "jfr/support/jfrResolution.hpp"
@ -43,6 +44,7 @@
#include "runtime/java.hpp"
#include "runtime/javaThread.hpp"
bool Jfr::is_enabled() {
return JfrRecorder::is_enabled();
}
@ -153,9 +155,9 @@ void Jfr::on_resolution(const Method* caller, const Method* target, TRAPS) {
}
#endif
void Jfr::on_vm_shutdown(bool emit_old_object_samples, bool emit_event_shutdown, bool halt) {
void Jfr::on_vm_shutdown(bool exception_handler /* false */, bool halt /* false */, bool oom /* false */) {
if (!halt && JfrRecorder::is_recording()) {
JfrEmergencyDump::on_vm_shutdown(emit_old_object_samples, emit_event_shutdown);
JfrEmergencyDump::on_vm_shutdown(exception_handler, oom);
}
}
@ -173,6 +175,12 @@ bool Jfr::on_start_flight_recording_option(const JavaVMOption** option, char* de
return JfrOptionSet::parse_start_flight_recording_option(option, delimiter);
}
void Jfr::on_report_java_out_of_memory() {
if (CrashOnOutOfMemoryError && JfrRecorder::is_recording()) {
JfrRecorderService::emit_leakprofiler_events_on_oom();
}
}
#if INCLUDE_CDS
void Jfr::on_restoration(const Klass* k, JavaThread* jt) {
assert(k != nullptr, "invariant");

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2026, 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
@ -71,7 +71,7 @@ class Jfr : AllStatic {
static void on_resolution(const Method* caller, const Method* target, TRAPS);
static void on_java_thread_start(JavaThread* starter, JavaThread* startee);
static void on_set_current_thread(JavaThread* jt, oop thread);
static void on_vm_shutdown(bool emit_old_object_samples, bool emit_event_shutdown, bool halt = false);
static void on_vm_shutdown(bool exception_handler = false, bool halt = false, bool oom = false);
static void on_vm_error_report(outputStream* st);
static bool on_flight_recorder_option(const JavaVMOption** option, char* delimiter);
static bool on_start_flight_recording_option(const JavaVMOption** option, char* delimiter);
@ -79,6 +79,7 @@ class Jfr : AllStatic {
static void initialize_main_thread(JavaThread* jt);
static bool has_sample_request(JavaThread* jt);
static void check_and_process_sample_request(JavaThread* jt);
static void on_report_java_out_of_memory();
CDS_ONLY(static void on_restoration(const Klass* k, JavaThread* jt);)
};

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2026, 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
@ -364,8 +364,7 @@ JVM_ENTRY_NO_ENV(void, jfr_set_force_instrumentation(JNIEnv* env, jclass jvm, jb
JVM_END
NO_TRANSITION(void, jfr_emit_old_object_samples(JNIEnv* env, jclass jvm, jlong cutoff_ticks, jboolean emit_all, jboolean skip_bfs))
JfrRecorderService service;
service.emit_leakprofiler_events(cutoff_ticks, emit_all == JNI_TRUE, skip_bfs == JNI_TRUE);
JfrRecorderService::emit_leakprofiler_events(cutoff_ticks, emit_all == JNI_TRUE, skip_bfs == JNI_TRUE);
NO_TRANSITION_END
JVM_ENTRY_NO_ENV(void, jfr_exclude_thread(JNIEnv* env, jclass jvm, jobject t))

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2026, 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
@ -232,41 +232,50 @@ void JfrSamplerThread::task_stacktrace(JfrSampleRequestType type, JavaThread** l
JavaThread* start = nullptr;
elapsedTimer sample_time;
sample_time.start();
ThreadsListHandle tlh;
// Resolve a sample session relative start position index into the thread list array.
// In cases where the last sampled thread is null or not-null but stale, find_index() returns -1.
_cur_index = tlh.list()->find_index_of_JavaThread(*last_thread);
JavaThread* current = _cur_index != -1 ? *last_thread : nullptr;
{
/*
* Take the Threads_lock for three purposes:
*
* 1) Avoid sampling right through a safepoint,
* which could result in touching oops in case of virtual threads.
* 2) Prevent JFR from issuing an epoch rotation while the sampler thread
* is actively processing a thread in state native, as both threads are outside the safepoint protocol.
* 3) Some operating systems (BSD / Mac) require a process lock when sending a signal with pthread_kill.
* Holding the Threads_lock prevents a JavaThread from calling os::create_thread(), which also takes the process lock.
* In a sense, we provide a coarse signal mask, so we can always send the resume signal.
*/
MutexLocker tlock(Threads_lock);
ThreadsListHandle tlh;
// Resolve a sample session relative start position index into the thread list array.
// In cases where the last sampled thread is null or not-null but stale, find_index() returns -1.
_cur_index = tlh.list()->find_index_of_JavaThread(*last_thread);
JavaThread* current = _cur_index != -1 ? *last_thread : nullptr;
while (num_samples < sample_limit) {
current = next_thread(tlh.list(), start, current);
if (current == nullptr) {
break;
}
if (is_excluded(current)) {
continue;
}
if (start == nullptr) {
start = current; // remember the thread where we started to attempt sampling
}
bool success;
if (JAVA_SAMPLE == type) {
success = sample_java_thread(current);
} else {
assert(type == NATIVE_SAMPLE, "invariant");
success = sample_native_thread(current);
}
if (success) {
num_samples++;
}
if (SafepointSynchronize::is_at_safepoint()) {
// For _thread_in_native, we cannot get the Threads_lock.
// For _thread_in_Java, well, there are none.
break;
while (num_samples < sample_limit) {
current = next_thread(tlh.list(), start, current);
if (current == nullptr) {
break;
}
if (is_excluded(current)) {
continue;
}
if (start == nullptr) {
start = current; // remember the thread where we started to attempt sampling
}
bool success;
if (JAVA_SAMPLE == type) {
success = sample_java_thread(current);
} else {
assert(type == NATIVE_SAMPLE, "invariant");
success = sample_native_thread(current);
}
if (success) {
num_samples++;
}
}
*last_thread = current; // remember the thread we last attempted to sample
}
*last_thread = current; // remember the thread we last attempted to sample
sample_time.stop();
log_trace(jfr)("JFR thread sampling done in %3.7f secs with %d java %d native samples",
sample_time.seconds(), type == JAVA_SAMPLE ? num_samples : 0, type == NATIVE_SAMPLE ? num_samples : 0);
@ -297,6 +306,7 @@ class OSThreadSampler : public SuspendedThreadTask {
// Sampling a thread in state _thread_in_Java
// involves a platform-specific thread suspend and CPU context retrieval.
bool JfrSamplerThread::sample_java_thread(JavaThread* jt) {
assert_lock_strong(Threads_lock);
if (jt->thread_state() != _thread_in_Java) {
return false;
}
@ -328,6 +338,7 @@ static JfrSamplerThread* _sampler_thread = nullptr;
// without thread suspension and CPU context retrieval,
// if we carefully order the loads of the thread state.
bool JfrSamplerThread::sample_native_thread(JavaThread* jt) {
assert_lock_strong(Threads_lock);
if (jt->thread_state() != _thread_in_native) {
return false;
}
@ -343,24 +354,14 @@ bool JfrSamplerThread::sample_native_thread(JavaThread* jt) {
SafepointMechanism::arm_local_poll_release(jt);
// Take the Threads_lock for two purposes:
// 1) Avoid sampling through a safepoint which could result
// in touching oops in case of virtual threads.
// 2) Prevent JFR from issuing an epoch rotation while the sampler thread
// is actively processing a thread in native, as both threads are now
// outside the safepoint protocol.
// OrderAccess::fence() as part of acquiring the lock prevents loads from floating up.
JfrMutexTryLock threads_lock(Threads_lock);
if (!threads_lock.acquired() || !jt->has_last_Java_frame()) {
// Remove the native sample request and release the potentially waiting thread.
JfrSampleMonitor jsm(tl);
return false;
// Separate the arming of the poll (above) from the reading of JavaThread state (below).
if (UseSystemMemoryBarrier) {
SystemMemoryBarrier::emit();
} else {
OrderAccess::fence();
}
if (jt->thread_state() != _thread_in_native) {
assert_lock_strong(Threads_lock);
if (jt->thread_state() != _thread_in_native || !jt->has_last_Java_frame()) {
JfrSampleMonitor jsm(tl);
if (jsm.is_waiting()) {
// The thread has already returned from native,

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2026, 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
@ -38,6 +38,8 @@
#include "runtime/mutexLocker.hpp"
#include "runtime/os.hpp"
#include "runtime/thread.inline.hpp"
#include "runtime/vmOperations.hpp"
#include "runtime/vmThread.hpp"
#include "utilities/growableArray.hpp"
#include "utilities/ostream.hpp"
@ -460,15 +462,6 @@ static void release_locks(Thread* thread) {
assert(thread != nullptr, "invariant");
assert(!thread->is_Java_thread() || JavaThread::cast(thread)->thread_state() == _thread_in_vm, "invariant");
#ifdef ASSERT
Mutex* owned_lock = thread->owned_locks();
while (owned_lock != nullptr) {
Mutex* next = owned_lock->next();
owned_lock->unlock();
owned_lock = next;
}
#endif // ASSERT
if (Threads_lock->owned_by_self()) {
Threads_lock->unlock();
}
@ -550,17 +543,14 @@ class JavaThreadInVMAndNative : public StackObj {
}
};
static void post_events(bool emit_old_object_samples, bool emit_event_shutdown, Thread* thread) {
if (emit_old_object_samples) {
LeakProfiler::emit_events(max_jlong, false, false);
}
if (emit_event_shutdown) {
static void post_events(bool exception_handler, bool oom, Thread * thread) {
if (exception_handler) {
EventShutdown e;
e.set_reason("VM Error");
e.set_reason(oom ? "CrashOnOutOfMemoryError" : "VM Error");
e.commit();
}
EventDumpReason event;
event.set_reason(emit_old_object_samples ? "Out of Memory" : "Crash");
event.set_reason(exception_handler && oom ? "CrashOnOutOfMemoryError" : exception_handler ? "Crash" : "Out of Memory");
event.set_recordingId(-1);
event.commit();
}
@ -594,20 +584,40 @@ static bool guard_reentrancy() {
return false;
}
void JfrEmergencyDump::on_vm_shutdown(bool emit_old_object_samples, bool emit_event_shutdown) {
void JfrEmergencyDump::on_vm_shutdown(bool exception_handler, bool oom) {
if (!guard_reentrancy()) {
return;
}
Thread* const thread = Thread::current_or_null_safe();
assert(thread != nullptr, "invariant");
if (thread->is_Watcher_thread()) {
log_info(jfr, system)("The Watcher thread crashed so no jfr emergency dump will be generated.");
return;
}
// Ensure a JavaThread is _thread_in_vm when we make this call
JavaThreadInVMAndNative jtivm(thread);
post_events(exception_handler, oom, thread);
if (thread->is_Watcher_thread()) {
// We cannot attempt an emergency dump using the Watcher thread
// because we rely on the WatcherThread task "is_error_reported()",
// to exit the VM after a hardcoded timeout, should the relatively
// risky operation of an emergency dump fail (deadlock, livelock).
log_warning(jfr, system)
("The Watcher thread crashed so no jfr emergency dump will be generated.");
return;
}
if (thread->is_VM_thread()) {
const VM_Operation* const operation = VMThread::vm_operation();
if (operation != nullptr && operation->type() == VM_Operation::VMOp_JFROldObject) {
// We will not be able to issue a rotation because the rotation lock
// is held by the JFR Recorder Thread that issued the VM_Operation.
log_warning(jfr, system)
("The VM Thread crashed as part of emitting leak profiler events so no jfr emergency dump will be generated.");
return;
}
}
release_locks(thread);
post_events(emit_old_object_samples, emit_event_shutdown, thread);
// if JavaThread, transition to _thread_in_native to issue a final flushpoint
NoHandleMark nhm;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2026, 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
@ -39,7 +39,7 @@ class JfrEmergencyDump : AllStatic {
static const char* chunk_path(const char* repository_path);
static void on_vm_error(const char* repository_path);
static void on_vm_error_report(outputStream* st, const char* repository_path);
static void on_vm_shutdown(bool emit_old_object_samples, bool emit_event_shutdown);
static void on_vm_shutdown(bool exception_handler, bool oom);
};
#endif // SHARE_JFR_RECORDER_REPOSITORY_JFREMERGENCYDUMP_HPP

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 2026, 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
@ -34,7 +34,8 @@
(MSGBIT(MSG_START)) | \
(MSGBIT(MSG_CLONE_IN_MEMORY)) | \
(MSGBIT(MSG_VM_ERROR)) | \
(MSGBIT(MSG_FLUSHPOINT)) \
(MSGBIT(MSG_FLUSHPOINT)) | \
(MSGBIT(MSG_EMIT_LEAKP_REFCHAINS)) \
)
static JfrPostBox* _instance = nullptr;
@ -165,7 +166,7 @@ void JfrPostBox::notify_waiters() {
assert(JfrMsg_lock->owned_by_self(), "incrementing _msg_handled_serial is protected by JfrMsg_lock.");
// Update made visible on release of JfrMsg_lock via fence instruction in Monitor::IUnlock.
++_msg_handled_serial;
JfrMsg_lock->notify();
JfrMsg_lock->notify_all();
}
// safeguard to ensure no threads are left waiting

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 2026, 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
@ -43,6 +43,7 @@ enum JFR_Msg {
MSG_SHUTDOWN,
MSG_VM_ERROR,
MSG_FLUSHPOINT,
MSG_EMIT_LEAKP_REFCHAINS,
MSG_NO_OF_MSGS
};
@ -51,23 +52,25 @@ enum JFR_Msg {
*
* Synchronous messages (posting thread waits for message completion):
*
* MSG_CLONE_IN_MEMORY (0) ; MSGBIT(MSG_CLONE_IN_MEMORY) == (1 << 0) == 0x1
* MSG_START(1) ; MSGBIT(MSG_START) == (1 << 0x1) == 0x2
* MSG_STOP (2) ; MSGBIT(MSG_STOP) == (1 << 0x2) == 0x4
* MSG_ROTATE (3) ; MSGBIT(MSG_ROTATE) == (1 << 0x3) == 0x8
* MSG_VM_ERROR (8) ; MSGBIT(MSG_VM_ERROR) == (1 << 0x8) == 0x100
* MSG_FLUSHPOINT (9) ; MSGBIT(MSG_FLUSHPOINT) == (1 << 0x9) == 0x200
* MSG_CLONE_IN_MEMORY (0) ; MSGBIT(MSG_CLONE_IN_MEMORY) == (1 << 0) == 0x1
* MSG_START(1) ; MSGBIT(MSG_START) == (1 << 0x1) == 0x2
* MSG_STOP (2) ; MSGBIT(MSG_STOP) == (1 << 0x2) == 0x4
* MSG_ROTATE (3) ; MSGBIT(MSG_ROTATE) == (1 << 0x3) == 0x8
* MSG_VM_ERROR (8) ; MSGBIT(MSG_VM_ERROR) == (1 << 0x8) == 0x100
* MSG_FLUSHPOINT (9) ; MSGBIT(MSG_FLUSHPOINT) == (1 << 0x9) == 0x200
* MSG_EMIT_LEAKP_REFCHAINS (10); MSGBIT(MSG_EMIT_LEAKP_REFCHAINS) == (1 << 0xa) == 0x400
*
* Asynchronous messages (posting thread returns immediately upon deposit):
*
* MSG_FULLBUFFER (4) ; MSGBIT(MSG_FULLBUFFER) == (1 << 0x4) == 0x10
* MSG_CHECKPOINT (5) ; MSGBIT(CHECKPOINT) == (1 << 0x5) == 0x20
* MSG_WAKEUP (6) ; MSGBIT(WAKEUP) == (1 << 0x6) == 0x40
* MSG_SHUTDOWN (7) ; MSGBIT(MSG_SHUTDOWN) == (1 << 0x7) == 0x80
* MSG_FULLBUFFER (4) ; MSGBIT(MSG_FULLBUFFER) == (1 << 0x4) == 0x10
* MSG_CHECKPOINT (5) ; MSGBIT(CHECKPOINT) == (1 << 0x5) == 0x20
* MSG_WAKEUP (6) ; MSGBIT(WAKEUP) == (1 << 0x6) == 0x40
* MSG_SHUTDOWN (7) ; MSGBIT(MSG_SHUTDOWN) == (1 << 0x7) == 0x80
*/
class JfrPostBox : public JfrCHeapObj {
friend class JfrRecorder;
friend class JfrRecorderService;
public:
void post(JFR_Msg msg);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2026, 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
@ -55,6 +55,7 @@
#include "runtime/safepoint.hpp"
#include "runtime/vmOperations.hpp"
#include "runtime/vmThread.hpp"
#include "utilities/growableArray.hpp"
// incremented on each flushpoint
static u8 flushpoint_id = 0;
@ -391,6 +392,7 @@ class JfrSafepointWriteVMOperation : public VM_Operation {
JfrRecorderService::JfrRecorderService() :
_checkpoint_manager(JfrCheckpointManager::instance()),
_chunkwriter(JfrRepository::chunkwriter()),
_post_box(JfrPostBox::instance()),
_repository(JfrRepository::instance()),
_stack_trace_repository(JfrStackTraceRepository::instance()),
_storage(JfrStorage::instance()),
@ -670,17 +672,173 @@ void JfrRecorderService::evaluate_chunk_size_for_rotation() {
JfrChunkRotation::evaluate(_chunkwriter);
}
void JfrRecorderService::emit_leakprofiler_events(int64_t cutoff_ticks, bool emit_all, bool skip_bfs) {
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(JavaThread::current()));
// Take the rotation lock to exclude flush() during event emits. This is because event emit
// also creates a number checkpoint events. Those checkpoint events require a future typeset checkpoint
// event for completeness, i.e. to be generated before being flushed to a segment.
// The upcoming flush() or rotation() after event emit completes this typeset checkpoint
// and serializes all event emit checkpoint events to the same segment.
JfrRotationLock lock;
// Take the rotation lock before the transition.
JavaThread* current_thread = JavaThread::current();
MACOS_AARCH64_ONLY(ThreadWXEnable __wx(WXWrite, current_thread));
ThreadInVMfromNative transition(current_thread);
LeakProfiler::emit_events(cutoff_ticks, emit_all, skip_bfs);
// LeakProfiler event serialization support.
struct JfrLeakProfilerEmitRequest {
int64_t cutoff_ticks;
bool emit_all;
bool skip_bfs;
bool oom;
};
typedef GrowableArrayCHeap<JfrLeakProfilerEmitRequest, mtTracing> JfrLeakProfilerEmitRequestQueue;
static JfrLeakProfilerEmitRequestQueue* _queue = nullptr;
constexpr const static int64_t _no_path_to_gc_roots = 0;
static bool _oom_emit_request_posted = false;
static bool _oom_emit_request_delivered = false;
static inline bool exclude_paths_to_gc_roots(int64_t cutoff_ticks) {
return cutoff_ticks <= _no_path_to_gc_roots;
}
static void enqueue(const JfrLeakProfilerEmitRequest& request) {
assert(JfrRotationLock::is_owner(), "invariant");
if (_queue == nullptr) {
_queue = new JfrLeakProfilerEmitRequestQueue(4);
}
assert(_queue != nullptr, "invariant");
assert(!_oom_emit_request_posted, "invariant");
if (request.oom) {
_oom_emit_request_posted = true;
}
_queue->append(request);
}
static JfrLeakProfilerEmitRequest dequeue() {
assert(JfrRotationLock::is_owner(), "invariant");
assert(_queue != nullptr, "invariant");
assert(_queue->is_nonempty(), "invariant");
const JfrLeakProfilerEmitRequest& request = _queue->first();
_queue->remove_at(0);
return request;
}
// This version of emit excludes path-to-gc-roots, i.e. it skips reference chains.
static void emit_leakprofiler_events(bool emit_all, bool skip_bfs, JavaThread* jt) {
assert(jt != nullptr, "invariant");
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(jt));
// Take the rotation lock to exclude flush() during event emits. This is because the event emit operation
// also creates a number of checkpoint events. Those checkpoint events require a future typeset checkpoint
// event for completeness, i.e., to be generated before being flushed to a segment.
// The upcoming flush() or rotation() after event emit completes this typeset checkpoint
// and serializes all checkpoint events to the same segment.
JfrRotationLock lock;
// Take the rotation lock before the thread transition, to avoid blocking safepoints.
if (_oom_emit_request_posted) {
// A request to emit leakprofiler events in response to CrashOnOutOfMemoryError
// is pending or has already been completed. We are about to crash at any time now.
assert(CrashOnOutOfMemoryError, "invariant");
return;
}
MACOS_AARCH64_ONLY(ThreadWXEnable __wx(WXWrite, jt));
ThreadInVMfromNative transition(jt);
// Since we are not requesting path-to-gc-roots, i.e., reference chains, we need not issue a VM_Operation.
// Therefore, we can let the requesting thread process the request directly, since it already holds the requisite lock.
LeakProfiler::emit_events(_no_path_to_gc_roots, emit_all, skip_bfs);
}
void JfrRecorderService::transition_and_post_leakprofiler_emit_msg(JavaThread* jt) {
assert(jt != nullptr, "invariant");
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(jt);)
assert(!JfrRotationLock::is_owner(), "invariant");
// Transition to _thread_in_VM and post a synchronous message to the JFR Recorder Thread
// for it to process our enqueued request, which includes paths-to-gc-roots, i.e., reference chains.
MACOS_AARCH64_ONLY(ThreadWXEnable __wx(WXWrite, jt));
ThreadInVMfromNative transition(jt);
_post_box.post(MSG_EMIT_LEAKP_REFCHAINS);
}
// This version of emit includes path-to-gc-roots, i.e., it includes in the request traversing of reference chains.
// Traversing reference chains is performed as part of a VM_Operation, and we initiate it from the JFR Recorder Thread.
// Because multiple threads can concurrently report_on_java_out_of_memory(), having them all post a synchronous JFR msg,
// they rendezvous at a safepoint in a convenient state, ThreadBlockInVM. This mechanism prevents any thread from racing past
// this point and begin executing VMError::report_and_die(), until at least one oom request has been delivered.
void JfrRecorderService::emit_leakprofiler_events_paths_to_gc_roots(int64_t cutoff_ticks,
bool emit_all,
bool skip_bfs,
bool oom,
JavaThread* jt) {
assert(jt != nullptr, "invariant");
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(jt);)
assert(!exclude_paths_to_gc_roots(cutoff_ticks), "invariant");
{
JfrRotationLock lock;
// Take the rotation lock to read and post a request for the JFR Recorder Thread.
if (_oom_emit_request_posted) {
if (!oom) {
// A request to emit leakprofiler events in response to CrashOnOutOfMemoryError
// is pending or has already been completed. We are about to crash at any time now.
assert(CrashOnOutOfMemoryError, "invariant");
return;
}
} else {
assert(!_oom_emit_request_posted, "invariant");
JfrLeakProfilerEmitRequest request = { cutoff_ticks, emit_all, skip_bfs, oom };
enqueue(request);
}
}
JfrRecorderService service;
service.transition_and_post_leakprofiler_emit_msg(jt);
}
// Leakprofiler serialization request, the jdk.jfr.internal.JVM.emitOldObjectSamples() Java entry point.
void JfrRecorderService::emit_leakprofiler_events(int64_t cutoff_ticks,
bool emit_all,
bool skip_bfs) {
JavaThread* const jt = JavaThread::current();
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(jt);)
if (exclude_paths_to_gc_roots(cutoff_ticks)) {
::emit_leakprofiler_events(emit_all, skip_bfs, jt);
return;
}
emit_leakprofiler_events_paths_to_gc_roots(cutoff_ticks, emit_all, skip_bfs, /* oom */ false, jt);
}
// Leakprofiler serialization request, the report_on_java_out_of_memory VM entry point.
void JfrRecorderService::emit_leakprofiler_events_on_oom() {
assert(CrashOnOutOfMemoryError, "invariant");
if (EventOldObjectSample::is_enabled()) {
JavaThread* const jt = JavaThread::current();
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(jt);)
ThreadToNativeFromVM transition(jt);
emit_leakprofiler_events_paths_to_gc_roots(max_jlong, false, false, /* oom */ true, jt);
}
}
// The worker routine for the JFR Recorder Thread when processing MSG_EMIT_LEAKP_REFCHAINS messages.
void JfrRecorderService::emit_leakprofiler_events() {
JavaThread* const jt = JavaThread::current();
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(jt));
// Take the rotation lock before the transition.
JfrRotationLock lock;
if (_oom_emit_request_delivered) {
// A request to emit leakprofiler events in response to CrashOnOutOfMemoryError
// has already been completed. We are about to crash at any time now.
assert(_oom_emit_request_posted, "invariant");
assert(CrashOnOutOfMemoryError, "invariant");
return;
}
assert(_queue->is_nonempty(), "invariant");
{
MACOS_AARCH64_ONLY(ThreadWXEnable __wx(WXWrite, jt));
ThreadInVMfromNative transition(jt);
while (_queue->is_nonempty()) {
const JfrLeakProfilerEmitRequest& request = dequeue();
LeakProfiler::emit_events(request.cutoff_ticks, request.emit_all, request.skip_bfs);
if (_oom_emit_request_posted && request.oom) {
assert(CrashOnOutOfMemoryError, "invariant");
_oom_emit_request_delivered = true;
break;
}
}
}
// If processing involved an out-of-memory request, issue an immediate flush operation.
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(jt));
if (_chunkwriter.is_valid() && _oom_emit_request_delivered) {
invoke_flush();
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2026, 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,19 +27,23 @@
#include "jfr/utilities/jfrAllocation.hpp"
class JavaThread;
class JfrCheckpointManager;
class JfrChunkWriter;
class JfrPostBox;
class JfrRepository;
class JfrStackTraceRepository;
class JfrStorage;
class JfrStringPool;
class JfrRecorderService : public StackObj {
friend class Jfr;
friend class JfrSafepointClearVMOperation;
friend class JfrSafepointWriteVMOperation;
private:
JfrCheckpointManager& _checkpoint_manager;
JfrChunkWriter& _chunkwriter;
JfrPostBox& _post_box;
JfrRepository& _repository;
JfrStackTraceRepository& _stack_trace_repository;
JfrStorage& _storage;
@ -64,6 +68,14 @@ class JfrRecorderService : public StackObj {
void invoke_safepoint_write();
void post_safepoint_write();
void transition_and_post_leakprofiler_emit_msg(JavaThread* jt);
static void emit_leakprofiler_events_on_oom();
static void emit_leakprofiler_events_paths_to_gc_roots(int64_t cutoff_ticks,
bool emit_all,
bool skip_bfs,
bool oom,
JavaThread* jt);
public:
JfrRecorderService();
void start();
@ -72,8 +84,12 @@ class JfrRecorderService : public StackObj {
void flushpoint();
void process_full_buffers();
void evaluate_chunk_size_for_rotation();
void emit_leakprofiler_events(int64_t cutoff_ticks, bool emit_all, bool skip_bfs);
void emit_leakprofiler_events();
static bool is_recording();
static void emit_leakprofiler_events(int64_t cutoff_ticks,
bool emit_all,
bool skip_bfs);
};
#endif // SHARE_JFR_RECORDER_SERVICE_JFRRECORDERSERVICE_HPP

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2026, 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
@ -44,6 +44,7 @@ void recorderthread_entry(JavaThread* thread, JavaThread* unused) {
#define ROTATE (msgs & (MSGBIT(MSG_ROTATE)|MSGBIT(MSG_STOP)))
#define FLUSHPOINT (msgs & (MSGBIT(MSG_FLUSHPOINT)))
#define PROCESS_FULL_BUFFERS (msgs & (MSGBIT(MSG_ROTATE)|MSGBIT(MSG_STOP)|MSGBIT(MSG_FULLBUFFER)))
#define LEAKPROFILER_REFCHAINS (msgs & MSGBIT(MSG_EMIT_LEAKP_REFCHAINS))
JfrPostBox& post_box = JfrRecorderThreadEntry::post_box();
log_debug(jfr, system)("Recorder thread STARTED");
@ -70,6 +71,9 @@ void recorderthread_entry(JavaThread* thread, JavaThread* unused) {
if (PROCESS_FULL_BUFFERS) {
service.process_full_buffers();
}
if (LEAKPROFILER_REFCHAINS) {
service.emit_leakprofiler_events();
}
// Check amount of data written to chunk already
// if it warrants asking for a new chunk.
service.evaluate_chunk_size_for_rotation();
@ -98,5 +102,5 @@ void recorderthread_entry(JavaThread* thread, JavaThread* unused) {
#undef ROTATE
#undef FLUSHPOINT
#undef PROCESS_FULL_BUFFERS
#undef SCAVENGE
#undef LEAKPROFILER_REFCHAINS
}

View File

@ -1031,6 +1031,15 @@ bool CallNode::is_call_to_arraycopystub() const {
return false;
}
bool CallNode::is_call_to_multianewarray_stub() const {
if (_name != nullptr &&
strstr(_name, "multianewarray") != nullptr &&
strstr(_name, "C2 runtime") != nullptr) {
return true;
}
return false;
}
//=============================================================================
uint CallJavaNode::size_of() const { return sizeof(*this); }
bool CallJavaNode::cmp( const Node &n ) const {

View File

@ -758,6 +758,7 @@ public:
virtual uint match_edge(uint idx) const;
bool is_call_to_arraycopystub() const;
bool is_call_to_multianewarray_stub() const;
virtual void copy_call_debug_info(PhaseIterGVN* phase, SafePointNode* sfpt) {}

View File

@ -1112,8 +1112,6 @@ Node *ModINode::Ideal(PhaseGVN *phase, bool can_reshape) {
if( !ti->is_con() ) return nullptr;
jint con = ti->get_con();
Node *hook = new Node(1);
// First, special check for modulo 2^k-1
if( con >= 0 && con < max_jint && is_power_of_2(con+1) ) {
uint k = exact_log2(con+1); // Extract k
@ -1129,7 +1127,9 @@ Node *ModINode::Ideal(PhaseGVN *phase, bool can_reshape) {
Node *x = in(1); // Value being mod'd
Node *divisor = in(2); // Also is mask
hook->init_req(0, x); // Add a use to x to prevent him from dying
// Add a use to x to prevent it from dying
Node* hook = new Node(1);
hook->init_req(0, x);
// Generate code to reduce X rapidly to nearly 2^k-1.
for( int i = 0; i < trip_count; i++ ) {
Node *xl = phase->transform( new AndINode(x,divisor) );
@ -1185,6 +1185,7 @@ Node *ModINode::Ideal(PhaseGVN *phase, bool can_reshape) {
}
// Save in(1) so that it cannot be changed or deleted
Node* hook = new Node(1);
hook->init_req(0, in(1));
// Divide using the transform from DivI to MulL
@ -1407,8 +1408,6 @@ Node *ModLNode::Ideal(PhaseGVN *phase, bool can_reshape) {
if( !tl->is_con() ) return nullptr;
jlong con = tl->get_con();
Node *hook = new Node(1);
// Expand mod
if(con >= 0 && con < max_jlong && is_power_of_2(con + 1)) {
uint k = log2i_exact(con + 1); // Extract k
@ -1426,13 +1425,15 @@ Node *ModLNode::Ideal(PhaseGVN *phase, bool can_reshape) {
Node *x = in(1); // Value being mod'd
Node *divisor = in(2); // Also is mask
hook->init_req(0, x); // Add a use to x to prevent him from dying
// Add a use to x to prevent it from dying
Node* hook = new Node(1);
hook->init_req(0, x);
// Generate code to reduce X rapidly to nearly 2^k-1.
for( int i = 0; i < trip_count; i++ ) {
Node *xl = phase->transform( new AndLNode(x,divisor) );
Node *xh = phase->transform( new RShiftLNode(x,phase->intcon(k)) ); // Must be signed
x = phase->transform( new AddLNode(xh,xl) );
hook->set_req(0, x); // Add a use to x to prevent him from dying
hook->set_req(0, x); // Add a use to x to prevent it from dying
}
// Generate sign-fixup code. Was original value positive?
@ -1482,6 +1483,8 @@ Node *ModLNode::Ideal(PhaseGVN *phase, bool can_reshape) {
}
// Save in(1) so that it cannot be changed or deleted
// Add a use to x to prevent him from dying
Node* hook = new Node(1);
hook->init_req(0, in(1));
// Divide using the transform from DivL to MulL

View File

@ -2066,8 +2066,7 @@ void ConnectionGraph::add_call_node(CallNode* call) {
// Use bytecode estimator to record whether the call's return value escapes.
ciMethod* meth = call->as_CallJava()->method();
if (meth == nullptr) {
const char* name = call->as_CallStaticJava()->_name;
assert(strncmp(name, "C2 Runtime multianewarray", 25) == 0, "TODO: add failed case check");
assert(call->as_CallStaticJava()->is_call_to_multianewarray_stub(), "TODO: add failed case check");
// Returns a newly allocated non-escaped object.
add_java_object(call, PointsToNode::NoEscape);
set_not_scalar_replaceable(ptnode_adr(call_idx) NOT_PRODUCT(COMMA "is result of multinewarray"));
@ -2775,8 +2774,7 @@ int ConnectionGraph::find_init_values_phantom(JavaObjectNode* pta) {
assert(pta->arraycopy_dst() || alloc->as_CallStaticJava(), "sanity");
#ifdef ASSERT
if (!pta->arraycopy_dst() && alloc->as_CallStaticJava()->method() == nullptr) {
const char* name = alloc->as_CallStaticJava()->_name;
assert(strncmp(name, "C2 Runtime multianewarray", 25) == 0, "sanity");
assert(alloc->as_CallStaticJava()->is_call_to_multianewarray_stub(), "sanity");
}
#endif
// Non-escaped allocation returned from Java or runtime call have unknown values in fields.

View File

@ -3913,7 +3913,6 @@ const Type* SCMemProjNode::Value(PhaseGVN* phase) const
LoadStoreNode::LoadStoreNode( Node *c, Node *mem, Node *adr, Node *val, const TypePtr* at, const Type* rt, uint required )
: Node(required),
_type(rt),
_adr_type(at),
_barrier_data(0)
{
init_req(MemNode::Control, c );
@ -3921,6 +3920,7 @@ LoadStoreNode::LoadStoreNode( Node *c, Node *mem, Node *adr, Node *val, const Ty
init_req(MemNode::Address, adr);
init_req(MemNode::ValueIn, val);
init_class_id(Class_LoadStore);
DEBUG_ONLY(_adr_type = at; adr_type();)
}
//------------------------------Value-----------------------------------------
@ -3944,6 +3944,11 @@ const Type* LoadStoreNode::Value(PhaseGVN* phase) const {
return bottom_type();
}
const TypePtr* LoadStoreNode::adr_type() const {
const TypePtr* cross_check = DEBUG_ONLY(_adr_type) NOT_DEBUG(nullptr);
return MemNode::calculate_adr_type(in(MemNode::Address)->bottom_type(), cross_check);
}
uint LoadStoreNode::ideal_reg() const {
return _type->ideal_reg();
}

View File

@ -797,11 +797,6 @@ public:
virtual int Opcode() const;
virtual bool is_CFG() const { return false; }
virtual const Type *bottom_type() const {return Type::MEMORY;}
virtual const TypePtr *adr_type() const {
Node* ctrl = in(0);
if (ctrl == nullptr) return nullptr; // node is dead
return ctrl->in(MemNode::Memory)->adr_type();
}
virtual uint ideal_reg() const { return 0;} // memory projections don't have a register
virtual const Type* Value(PhaseGVN* phase) const;
#ifndef PRODUCT
@ -814,9 +809,11 @@ public:
class LoadStoreNode : public Node {
private:
const Type* const _type; // What kind of value is loaded?
const TypePtr* _adr_type; // What kind of memory is being addressed?
uint8_t _barrier_data; // Bit field with barrier information
virtual uint size_of() const; // Size is bigger
#ifdef ASSERT
const TypePtr* _adr_type; // What kind of memory is being addressed?
#endif // ASSERT
public:
LoadStoreNode( Node *c, Node *mem, Node *adr, Node *val, const TypePtr* at, const Type* rt, uint required );
virtual bool depends_only_on_test() const { return false; }
@ -824,7 +821,7 @@ public:
virtual const Type *bottom_type() const { return _type; }
virtual uint ideal_reg() const;
virtual const class TypePtr *adr_type() const { return _adr_type; } // returns bottom_type of address
virtual const TypePtr* adr_type() const;
virtual const Type* Value(PhaseGVN* phase) const;
bool result_not_used() const;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2007, 2026, 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
@ -2482,6 +2482,11 @@ static bool can_subword_truncate(Node* in, const Type* type) {
return false;
}
// Since casts specifically change the type of a node, stay on the safe side and do not truncate them.
if (in->is_ConstraintCast()) {
return false;
}
// Cannot be truncated:
switch (opc) {
case Op_AbsI:

View File

@ -75,6 +75,7 @@
// v.release_store(x) -> void
// v.release_store_fence(x) -> void
// v.compare_exchange(x, y [, o]) -> T
// v.compare_set(x, y [, o]) -> bool
// v.exchange(x [, o]) -> T
//
// (2) All atomic types are default constructible.
@ -267,6 +268,11 @@ public:
return AtomicAccess::cmpxchg(value_ptr(), compare_value, new_value, order);
}
bool compare_set(T compare_value, T new_value,
atomic_memory_order order = memory_order_conservative) {
return compare_exchange(compare_value, new_value, order) == compare_value;
}
T exchange(T new_value,
atomic_memory_order order = memory_order_conservative) {
return AtomicAccess::xchg(this->value_ptr(), new_value, order);
@ -479,6 +485,13 @@ public:
order));
}
bool compare_set(T compare_value, T new_value,
atomic_memory_order order = memory_order_conservative) {
return _value.compare_set(decay(compare_value),
decay(new_value),
order);
}
T exchange(T new_value, atomic_memory_order order = memory_order_conservative) {
return recover(_value.exchange(decay(new_value), order));
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2026, 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
@ -466,10 +466,7 @@ void before_exit(JavaThread* thread, bool halt) {
event.commit();
}
// 2nd argument (emit_event_shutdown) should be set to false
// because EventShutdown would be emitted at Threads::destroy_vm().
// (one of the callers of before_exit())
JFR_ONLY(Jfr::on_vm_shutdown(true, false, halt);)
JFR_ONLY(Jfr::on_vm_shutdown(false, halt);)
// Stop the WatcherThread. We do this before disenrolling various
// PeriodicTasks to reduce the likelihood of races.

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2026, 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
@ -25,9 +25,24 @@
#ifndef SHARE_RUNTIME_PREFETCH_INLINE_HPP
#define SHARE_RUNTIME_PREFETCH_INLINE_HPP
#include "runtime/prefetch.hpp"
#include "memory/allStatic.hpp"
#include "utilities/macros.hpp"
// If calls to prefetch methods are in a loop, the loop should be cloned
// such that if Prefetch{Scan,Copy}Interval and/or PrefetchFieldInterval
// say not to do prefetching, these methods aren't called. At the very
// least, they take up a memory issue slot. They should be implemented
// as inline assembly code: doing an actual call isn't worth the cost.
class Prefetch : AllStatic {
public:
// Prefetch anticipating read; must not fault, semantically a no-op
static void read(const void* loc, intx interval);
// Prefetch anticipating write; must not fault, semantically a no-op
static void write(void* loc, intx interval);
};
#include OS_CPU_HEADER_INLINE(prefetch)
#endif // SHARE_RUNTIME_PREFETCH_INLINE_HPP

View File

@ -191,8 +191,7 @@
//
// C2 stub blob/field names
//
// C2 stubs are provided with names in the format "C2 Runtime
// <stubname> _blob".
// C2 stubs are provided with names in the format "<stubname>_blob (C2 runtime)".
//
// A stub creation method OptoRuntime::generate(ciEnv* env) is
// generated which invokes the C2 compiler to generate each stub in

View File

@ -479,7 +479,7 @@ void StubInfo::process_stubgen_entry(StubGroup& group_cursor,
#define PROCESS_SHARED_BLOB(name, type) \
process_shared_blob(_group_cursor, _blob_cursor, \
_stub_cursor, _entry_cursor, \
"Shared Runtime " # name "_blob", \
#name "_blob (shared runtime)", \
BlobId:: JOIN3(shared, name, id), \
StubId:: JOIN3(shared, name, id), \
EntryId:: JOIN3(shared, name, id), \
@ -488,7 +488,7 @@ void StubInfo::process_stubgen_entry(StubGroup& group_cursor,
#define PROCESS_C1_BLOB(name) \
process_c1_blob(_group_cursor, _blob_cursor, \
_stub_cursor, _entry_cursor, \
"C1 Runtime " # name "_blob", \
#name "_blob (C1 runtime)", \
BlobId:: JOIN3(c1, name, id), \
StubId:: JOIN3(c1, name, id), \
EntryId:: JOIN3(c1, name, id)); \
@ -496,7 +496,7 @@ void StubInfo::process_stubgen_entry(StubGroup& group_cursor,
#define PROCESS_C2_BLOB(name, type) \
process_c2_blob(_group_cursor, _blob_cursor, \
_stub_cursor, _entry_cursor, \
"C2 Runtime " # name "_blob", \
#name "_blob (C2 runtime)", \
BlobId:: JOIN3(c2, name, id), \
StubId:: JOIN3(c2, name, id), \
EntryId:: JOIN3(c2, name, id)); \
@ -504,7 +504,7 @@ void StubInfo::process_stubgen_entry(StubGroup& group_cursor,
#define PROCESS_C2_STUB(name, fancy_jump, pass_tls, return_pc) \
process_c2_blob(_group_cursor, _blob_cursor, \
_stub_cursor, _entry_cursor, \
"C2 Runtime " # name "_blob", \
#name "_blob (C2 runtime)", \
BlobId:: JOIN3(c2, name, id), \
StubId:: JOIN3(c2, name, id), \
EntryId:: JOIN3(c2, name, id)); \
@ -512,20 +512,20 @@ void StubInfo::process_stubgen_entry(StubGroup& group_cursor,
#define PROCESS_STUBGEN_BLOB(blob) \
process_stubgen_blob(_group_cursor, _blob_cursor, \
_stub_cursor, _entry_cursor, \
"Stub Generator " # blob "_blob", \
#blob "_blob (stub gen)", \
BlobId:: JOIN3(stubgen, blob, id)); \
#define PROCESS_STUBGEN_STUB(blob, stub) \
process_stubgen_stub(_group_cursor, _blob_cursor, \
_stub_cursor, _entry_cursor, \
"Stub Generator " # stub "_stub", \
#stub "_stub (stub gen)", \
BlobId:: JOIN3(stubgen, blob, id), \
StubId:: JOIN3(stubgen, stub, id)); \
#define PROCESS_STUBGEN_ENTRY(blob, stub, field_name, getter_name) \
process_stubgen_entry(_group_cursor, _blob_cursor, \
_stub_cursor, _entry_cursor, \
"Stub Generator " # field_name "_entry", \
#field_name "_entry (stub gen)", \
BlobId:: JOIN3(stubgen, blob, id), \
StubId:: JOIN3(stubgen, stub, id), \
EntryId:: JOIN3(stubgen, field_name, id), \
@ -535,7 +535,7 @@ void StubInfo::process_stubgen_entry(StubGroup& group_cursor,
init_funcion) \
process_stubgen_entry(_group_cursor, _blob_cursor, \
_stub_cursor, _entry_cursor, \
"Stub Generator " # field_name "_entry", \
#field_name "_entry (stub gen)", \
BlobId:: JOIN3(stubgen, blob, id), \
StubId:: JOIN3(stubgen, stub, id), \
EntryId:: JOIN3(stubgen, field_name, id), \
@ -545,7 +545,7 @@ void StubInfo::process_stubgen_entry(StubGroup& group_cursor,
count) \
process_stubgen_entry(_group_cursor, _blob_cursor, \
_stub_cursor, _entry_cursor, \
"Stub Generator " # field_name "_entry", \
#field_name "_entry (stub gen)", \
BlobId:: JOIN3(stubgen, blob, id), \
StubId:: JOIN3(stubgen, stub, id), \
EntryId:: JOIN3(stubgen, field_name, id), \
@ -567,7 +567,7 @@ void StubInfo::process_stubgen_entry(StubGroup& group_cursor,
init_function) \
process_stubgen_entry(_group_cursor, _blob_cursor, \
_stub_cursor, _entry_cursor, \
"Stub Generator " # arch_name "_" # field_name "_entry", \
#arch_name "_" # field_name "_entry (stub gen)",\
BlobId:: JOIN3(stubgen, blob, id), \
StubId:: JOIN3(stubgen, stub, id), \
EntryId:: JOIN4(stubgen, arch_name, \

View File

@ -157,7 +157,7 @@ inline bool ConcurrentHashTable<CONFIG, MT>::
if (is_locked()) {
return false;
}
if (_first.compare_exchange(expect, node) == expect) {
if (_first.compare_set(expect, node)) {
return true;
}
return false;
@ -172,7 +172,7 @@ inline bool ConcurrentHashTable<CONFIG, MT>::
}
// We will expect a clean first pointer.
Node* tmp = first();
if (_first.compare_exchange(tmp, set_state(tmp, STATE_LOCK_BIT)) == tmp) {
if (_first.compare_set(tmp, set_state(tmp, STATE_LOCK_BIT))) {
return true;
}
return false;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2026, 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
@ -63,6 +63,9 @@
#include "utilities/nativeStackPrinter.hpp"
#include "utilities/unsigned5.hpp"
#include "utilities/vmError.hpp"
#if INCLUDE_JFR
#include "jfr/jfr.hpp"
#endif
#include <stdarg.h>
#include <stdio.h>
@ -262,6 +265,8 @@ void report_untested(const char* file, int line, const char* message) {
void report_java_out_of_memory(const char* message) {
static int out_of_memory_reported = 0;
JFR_ONLY(Jfr::on_report_java_out_of_memory();)
// A number of threads may attempt to report OutOfMemoryError at around the
// same time. To avoid dumping the heap or executing the data collection
// commands multiple times we just do it once when the first threads reports

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2017, 2024 SAP SE. All rights reserved.
* Copyright (c) 2023, 2025, Red Hat, Inc. and/or its affiliates.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
@ -1898,7 +1898,7 @@ void VMError::report_and_die(int id, const char* message, const char* detail_fmt
log.set_fd(-1);
}
JFR_ONLY(Jfr::on_vm_shutdown(static_cast<VMErrorType>(_id) == OOM_JAVA_HEAP_FATAL, true);)
JFR_ONLY(Jfr::on_vm_shutdown(true, false, static_cast<VMErrorType>(_id) == OOM_JAVA_HEAP_FATAL);)
if (PrintNMTStatistics) {
fdStream fds(fd_out);

View File

@ -181,7 +181,7 @@ void GenericWaitBarrier::Cell::disarm(int32_t expected_tag) {
tag, waiters);
int64_t new_state = encode(0, waiters);
if (_state.compare_exchange(state, new_state) == state) {
if (_state.compare_set(state, new_state)) {
// Successfully disarmed.
break;
}
@ -218,7 +218,7 @@ void GenericWaitBarrier::Cell::wait(int32_t expected_tag) {
tag, waiters);
int64_t new_state = encode(tag, waiters + 1);
if (_state.compare_exchange(state, new_state) == state) {
if (_state.compare_set(state, new_state)) {
// Success! Proceed to wait.
break;
}
@ -247,7 +247,7 @@ void GenericWaitBarrier::Cell::wait(int32_t expected_tag) {
tag, waiters);
int64_t new_state = encode(tag, waiters - 1);
if (_state.compare_exchange(state, new_state) == state) {
if (_state.compare_set(state, new_state)) {
// Success!
break;
}

View File

@ -2045,19 +2045,26 @@ public final class String
return encode(Charset.defaultCharset(), coder(), value);
}
boolean bytesCompatible(Charset charset) {
boolean bytesCompatible(Charset charset, int srcIndex, int numChars) {
if (isLatin1()) {
if (charset == ISO_8859_1.INSTANCE) {
return true; // ok, same encoding
} else if (charset == UTF_8.INSTANCE || charset == US_ASCII.INSTANCE) {
return !StringCoding.hasNegatives(value, 0, value.length); // ok, if ASCII-compatible
return !StringCoding.hasNegatives(value, srcIndex, numChars); // ok, if ASCII-compatible
}
}
return false;
}
void copyToSegmentRaw(MemorySegment segment, long offset) {
MemorySegment.copy(value, 0, segment, ValueLayout.JAVA_BYTE, offset, value.length);
void copyToSegmentRaw(MemorySegment segment, long offset, int srcIndex, int srcLength) {
if (!isLatin1()) {
// This method is intended to be used together with bytesCompatible, which currently only supports
// latin1 strings. In the future, bytesCompatible could be updated to handle more cases, like
// UTF-16 strings (when the platform and charset endianness match, and the String doesnt contain
// unpaired surrogates). If that happens, copyToSegmentRaw should also be updated.
throw new IllegalStateException("This string does not support copyToSegmentRaw");
}
MemorySegment.copy(value, srcIndex, segment, ValueLayout.JAVA_BYTE, offset, srcLength);
}
/**

View File

@ -2331,13 +2331,13 @@ public final class System {
}
@Override
public void copyToSegmentRaw(String string, MemorySegment segment, long offset) {
string.copyToSegmentRaw(segment, offset);
public void copyToSegmentRaw(String string, MemorySegment segment, long offset, int srcIndex, int srcLength) {
string.copyToSegmentRaw(segment, offset, srcIndex, srcLength);
}
@Override
public boolean bytesCompatible(String string, Charset charset) {
return string.bytesCompatible(charset);
public boolean bytesCompatible(String string, Charset charset, int srcIndex, int numChars) {
return string.bytesCompatible(charset, srcIndex, numChars);
}
});
}

View File

@ -31,6 +31,8 @@ import java.lang.classfile.Instruction;
import java.lang.classfile.Opcode;
import jdk.internal.classfile.impl.AbstractInstruction;
import jdk.internal.classfile.impl.BytecodeHelpers;
import jdk.internal.classfile.impl.Util;
/**
* Models a local variable increment instruction in the {@code code} array of a
@ -82,6 +84,37 @@ public sealed interface IncrementInstruction extends Instruction
* @throws IllegalArgumentException if {@code slot} or {@code constant} is out of range
*/
static IncrementInstruction of(int slot, int constant) {
return new AbstractInstruction.UnboundIncrementInstruction(slot, constant);
var opcode = BytecodeHelpers.validateAndIsWideIinc(slot, constant) ? Opcode.IINC_W: Opcode.IINC;
return new AbstractInstruction.UnboundIncrementInstruction(opcode, slot, constant);
}
/**
* {@return an increment instruction}
* <p>
* {@code slot} must be {@link java.lang.classfile##u1 u1} and
* {@code constant} must be within {@code [-128, 127]} for
* {@link Opcode#IINC iinc}, or {@code slot} must be
* {@link java.lang.classfile##u2 u2} and {@code constant} must be
* within {@code [-32768, 32767]} for {@link Opcode#IINC_W wide iinc}.
*
* @apiNote
* The explicit {@code op} argument allows creating {@code wide} or
* regular increment instructions when {@code slot} and
* {@code constant} can be encoded with more optimized
* increment instructions.
*
* @param op the opcode for the specific type of increment instruction,
* which must be of kind {@link Opcode.Kind#INCREMENT}
* @param slot the local variable slot to increment
* @param constant the increment constant
* @throws IllegalArgumentException if the opcode kind is not
* {@link Opcode.Kind#INCREMENT} or {@code slot} or
* {@code constant} is out of range
* @since 27
*/
static IncrementInstruction of(Opcode op, int slot, int constant) {
Util.checkKind(op, Opcode.Kind.INCREMENT);
BytecodeHelpers.validateIncrement(op, slot, constant);
return new AbstractInstruction.UnboundIncrementInstruction(op, slot, constant);
}
}

View File

@ -1296,12 +1296,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl {
* over the decoding process is required.
* <p>
* Getting a string from a segment with a known byte offset and
* known byte length can be done like so:
* {@snippet lang=java :
* byte[] bytes = new byte[length];
* MemorySegment.copy(segment, JAVA_BYTE, offset, bytes, 0, length);
* return new String(bytes, charset);
* }
* known byte length can be done using {@link #getString(long, Charset, long)}.
*
* @param offset offset in bytes (relative to this segment address) at which this
* access operation will occur
@ -1328,6 +1323,41 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl {
*/
String getString(long offset, Charset charset);
/**
* Reads a string from this segment at the given offset, using the provided length
* and charset.
* <p>
* This method always replaces malformed-input and unmappable-character
* sequences with this charset's default replacement string. The {@link
* java.nio.charset.CharsetDecoder} class should be used when more control
* over the decoding process is required.
* <p>
* If the string contains any {@code '\0'} characters, they will be read as well.
* This differs from {@link #getString(long, Charset)}, which will only read up
* to the first {@code '\0'}, resulting in truncation for string data that contains
* the {@code '\0'} character.
*
* @param offset offset in bytes (relative to this segment address) at which this
* access operation will occur
* @param charset the charset used to {@linkplain Charset#newDecoder() decode} the
* string bytes
* @param byteLength length, in bytes, of the region of memory to read and decode into
* a string
* @return a Java string constructed from the bytes read from the given starting
* address up to the given length
* @throws IllegalArgumentException if the size of the string is greater than the
* largest string supported by the platform
* @throws IndexOutOfBoundsException if {@code offset < 0}
* @throws IndexOutOfBoundsException if {@code offset > byteSize() - byteLength}
* @throws IllegalStateException if the {@linkplain #scope() scope} associated with
* this segment is not {@linkplain Scope#isAlive() alive}
* @throws WrongThreadException if this method is called from a thread {@code T},
* such that {@code isAccessibleBy(T) == false}
* @throws IllegalArgumentException if {@code byteLength < 0}
* @since 27
*/
String getString(long offset, Charset charset, long byteLength);
/**
* Writes the given string into this segment at the given offset, converting it to
* a null-terminated byte sequence using the {@linkplain StandardCharsets#UTF_8 UTF-8}
@ -1366,7 +1396,8 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl {
* If the given string contains any {@code '\0'} characters, they will be
* copied as well. This means that, depending on the method used to read
* the string, such as {@link MemorySegment#getString(long)}, the string
* will appear truncated when read again.
* will appear truncated when read again. The string can be read without
* truncation using {@link #getString(long, Charset, long)}.
*
* @param offset offset in bytes (relative to this segment address) at which this
* access operation will occur, the final address of this write
@ -2606,6 +2637,51 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl {
elementCount);
}
/**
* Copies the byte sequence of the given string encoded using the provided charset
* to the destination segment.
* <p>
* This method always replaces malformed-input and unmappable-character
* sequences with this charset's default replacement string. The {@link
* java.nio.charset.CharsetDecoder} class should be used when more control
* over the decoding process is required.
* <p>
* If the given string contains any {@code '\0'} characters, they will be
* copied as well. This means that, depending on the method used to read
* the string, such as {@link MemorySegment#getString(long)}, the string
* will appear truncated when read again. The string can be read without
* truncation using {@link #getString(long, Charset, long)}.
*
* @param src the Java string to be written into the destination segment
* @param dstEncoding the charset used to {@linkplain Charset#newEncoder() encode}
* the string bytes.
* @param srcIndex the starting character index of the source string
* @param dst the destination segment
* @param dstOffset the starting offset, in bytes, of the destination segment
* @param numChars the number of characters to be copied
* @throws IllegalStateException if the {@linkplain #scope() scope} associated with
* {@code dst} is not {@linkplain Scope#isAlive() alive}
* @throws WrongThreadException if this method is called from a thread {@code T},
* such that {@code dst.isAccessibleBy(T) == false}
* @throws IndexOutOfBoundsException if either {@code srcIndex}, {@code numChars}, or {@code dstOffset}
* are {@code < 0}
* @throws IndexOutOfBoundsException if {@code srcIndex > src.length() - numChars}
* @throws IllegalArgumentException if {@code dst} is {@linkplain #isReadOnly() read-only}
* @throws IndexOutOfBoundsException if {@code dstOffset > dstSegment.byteSize() - B} where {@code B} is the size,
* in bytes, of the substring of {@code src} encoded using the given charset
* @return the number of copied bytes.
* @since 27
*/
@ForceInline
static long copy(String src, Charset dstEncoding, int srcIndex, MemorySegment dst, long dstOffset, int numChars) {
Objects.requireNonNull(src);
Objects.requireNonNull(dstEncoding);
Objects.requireNonNull(dst);
Objects.checkFromIndexSize(srcIndex, numChars, src.length());
return AbstractMemorySegmentImpl.copy(src, dstEncoding, srcIndex, dst, dstOffset, numChars);
}
/**
* Finds and returns the relative offset, in bytes, of the first mismatch between the
* source and the destination segments. More specifically, the bytes at offset

View File

@ -111,7 +111,8 @@ public interface SegmentAllocator {
* If the given string contains any {@code '\0'} characters, they will be
* copied as well. This means that, depending on the method used to read
* the string, such as {@link MemorySegment#getString(long)}, the string
* will appear truncated when read again.
* will appear truncated when read again. The string can be read without
* truncation using {@link MemorySegment#getString(long, Charset, long)}.
*
* @param str the Java string to be converted into a C string
* @param charset the charset used to {@linkplain Charset#newEncoder() encode} the
@ -137,10 +138,10 @@ public interface SegmentAllocator {
int termCharSize = StringSupport.CharsetKind.of(charset).terminatorCharSize();
MemorySegment segment;
int length;
if (StringSupport.bytesCompatible(str, charset)) {
if (StringSupport.bytesCompatible(str, charset, 0, str.length())) {
length = str.length();
segment = allocateNoInit((long) length + termCharSize);
StringSupport.copyToSegmentRaw(str, segment, 0);
StringSupport.copyToSegmentRaw(str, segment, 0, 0, str.length());
} else {
byte[] bytes = str.getBytes(charset);
length = bytes.length;
@ -153,6 +154,54 @@ public interface SegmentAllocator {
return segment;
}
/**
* Encodes a Java string using the provided charset and stores the resulting
* byte array into a memory segment.
* <p>
* This method always replaces malformed-input and unmappable-character
* sequences with this charset's default replacement byte array. The
* {@link java.nio.charset.CharsetEncoder} class should be used when more
* control over the encoding process is required.
* <p>
* If the given string contains any {@code '\0'} characters, they will be
* copied as well. This means that, depending on the method used to read
* the string, such as {@link MemorySegment#getString(long)}, the string
* will appear truncated when read again. The string can be read without
* truncation using {@link MemorySegment#getString(long, Charset, long)}.
*
* @param str the Java string to be encoded
* @param charset the charset used to {@linkplain Charset#newEncoder() encode} the
* string bytes
* @param srcIndex the starting index of the source string
* @param numChars the number of characters to be copied
* @return a new native segment containing the encoded string
* @throws IndexOutOfBoundsException if either {@code srcIndex} or {@code numChars} are {@code < 0}
* @throws IndexOutOfBoundsException if {@code srcIndex > str.length() - numChars}
*
* @implSpec The default implementation for this method copies the contents of the
* provided Java string into a new memory segment obtained by calling
* {@code this.allocate(B)}, where {@code B} is the size, in bytes, of
* the string encoded using the provided charset
* (e.g. {@code str.getBytes(charset).length});
* @since 27
*/
@ForceInline
default MemorySegment allocateFrom(String str, Charset charset, int srcIndex, int numChars) {
Objects.requireNonNull(charset);
Objects.requireNonNull(str);
Objects.checkFromIndexSize(srcIndex, numChars, str.length());
MemorySegment segment;
if (StringSupport.bytesCompatible(str, charset, srcIndex, numChars)) {
segment = allocateNoInit(numChars);
StringSupport.copyToSegmentRaw(str, segment, 0, srcIndex, numChars);
} else {
byte[] bytes = str.substring(srcIndex, srcIndex + numChars).getBytes(charset);
segment = allocateNoInit(bytes.length);
MemorySegment.copy(bytes, 0, segment, ValueLayout.JAVA_BYTE, 0, bytes.length);
}
return segment;
}
/**
* {@return a new memory segment initialized with the provided byte value}
* <p>

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1996, 2026, 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
@ -79,7 +79,11 @@ public class GZIPInputStream extends InflaterInputStream {
super(in, createInflater(in, size), size);
usesDefaultInflater = true;
try {
readHeader(in);
// we don't expect the stream to be at EOF
// and if it is, then we want readHeader to
// raise an exception, so we pass "true" for
// the "failOnEOF" param.
readHeader(in, true);
} catch (IOException ioe) {
this.inf.end();
throw ioe;
@ -190,12 +194,40 @@ public class GZIPInputStream extends InflaterInputStream {
/*
* Reads GZIP member header and returns the total byte number
* of this member header.
* If failOnEOF is false and if the given InputStream has already
* reached EOF when this method was invoked, then this method returns
* -1 (indicating that there's no GZIP member header).
* In all other cases of malformed header or EOF being detected
* when reading the header, this method will throw an IOException.
*/
private int readHeader(InputStream this_in) throws IOException {
private int readHeader(InputStream this_in, boolean failOnEOF) throws IOException {
CheckedInputStream in = new CheckedInputStream(this_in, crc);
crc.reset();
int magic;
if (!failOnEOF) {
// read an unsigned short value representing the GZIP magic header.
// this is the same as calling readUShort(in), except that here,
// when reading the first byte, we don't raise an EOFException
// if the stream has already reached EOF.
// read unsigned byte
int b = in.read();
if (b == -1) { // EOF
crc.reset();
return -1; // represents no header bytes available
}
checkUnexpectedByte(b);
// read the next unsigned byte to form the unsigned
// short. we throw the usual EOFException/ZipException
// from this point on if there is no more data or
// the data doesn't represent a header.
magic = (readUByte(in) << 8) | b;
} else {
magic = readUShort(in);
}
// Check header magic
if (readUShort(in) != GZIP_MAGIC) {
if (magic != GZIP_MAGIC) {
throw new ZipException("Not in GZIP format");
}
// Check compression method
@ -261,7 +293,11 @@ public class GZIPInputStream extends InflaterInputStream {
// try concatenated case
int m = 8; // this.trailer
try {
m += readHeader(in); // next.header
int numNextHeaderBytes = readHeader(in, false); // next.header (if available)
if (numNextHeaderBytes == -1) {
return true; // end of stream reached
}
m += numNextHeaderBytes;
} catch (IOException ze) {
return true; // ignore any malformed, do nothing
}
@ -295,14 +331,18 @@ public class GZIPInputStream extends InflaterInputStream {
if (b == -1) {
throw new EOFException();
}
if (b < -1 || b > 255) {
// Report on this.in, not argument in; see read{Header, Trailer}.
throw new IOException(this.in.getClass().getName()
+ ".read() returned value out of range -1..255: " + b);
}
checkUnexpectedByte(b);
return b;
}
private void checkUnexpectedByte(final int b) throws IOException {
if (b < -1 || b > 255) {
// report the InputStream type which returned this unexpected byte
throw new IOException(this.in.getClass().getName()
+ ".read() returned value out of range -1..255: " + b);
}
}
private byte[] tmpbuf = new byte[128];
/*

View File

@ -634,10 +634,10 @@ public interface JavaLangAccess {
/**
* Copy the string bytes to an existing segment, avoiding intermediate copies.
*/
void copyToSegmentRaw(String string, MemorySegment segment, long offset);
void copyToSegmentRaw(String string, MemorySegment segment, long offset, int srcIndex, int srcLength);
/**
* Are the string bytes compatible with the given charset?
*/
boolean bytesCompatible(String string, Charset charset);
boolean bytesCompatible(String string, Charset charset, int srcIndex, int numChars);
}

View File

@ -832,10 +832,8 @@ public abstract sealed class AbstractInstruction
final int slot;
final int constant;
public UnboundIncrementInstruction(int slot, int constant) {
super(BytecodeHelpers.validateAndIsWideIinc(slot, constant)
? Opcode.IINC_W
: Opcode.IINC);
public UnboundIncrementInstruction(Opcode op, int slot, int constant) {
super(op);
this.slot = slot;
this.constant = constant;
}

View File

@ -450,6 +450,13 @@ public class BytecodeHelpers {
return ret;
}
public static void validateIncrement(Opcode opcode, int slot, int constant) {
if (validateAndIsWideIinc(slot, constant) && opcode != Opcode.IINC_W) {
throw new IllegalArgumentException(
"IINC: operands require wide encoding for %s".formatted(opcode));
}
}
public static void validateRet(Opcode opcode, int slot) {
if (opcode == Opcode.RET && (slot & ~0xFF) == 0 ||
opcode == Opcode.RET_W && (slot & ~0xFFFF) == 0)

View File

@ -551,6 +551,13 @@ public abstract sealed class AbstractMemorySegmentImpl
unsafeGetOffset() == that.unsafeGetOffset();
}
@Override
public String getString(long offset, Charset charset, long byteLength) {
Utils.checkNonNegativeArgument(byteLength, "byteLength");
Objects.requireNonNull(charset);
return StringSupport.read(this, offset, charset, byteLength);
}
@Override
public int hashCode() {
return Objects.hash(
@ -702,6 +709,16 @@ public abstract sealed class AbstractMemorySegmentImpl
}
}
@ForceInline
public static long copy(String src, Charset dstEncoding, int srcIndex, MemorySegment dst, long dstOffset, int numChars) {
Objects.requireNonNull(src);
Objects.requireNonNull(dstEncoding);
Objects.requireNonNull(dst);
AbstractMemorySegmentImpl destImpl = (AbstractMemorySegmentImpl)dst;
return StringSupport.copyBytes(src, destImpl, dstEncoding, dstOffset, srcIndex, numChars);
}
// accessors
@ForceInline

View File

@ -30,11 +30,14 @@ import jdk.internal.access.SharedSecrets;
import jdk.internal.misc.ScopedMemoryAccess;
import jdk.internal.util.Architecture;
import jdk.internal.util.ArraysSupport;
import jdk.internal.util.Preconditions;
import jdk.internal.vm.annotation.ForceInline;
import java.lang.foreign.MemorySegment;
import java.lang.reflect.Array;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.util.Objects;
import static java.lang.foreign.ValueLayout.*;
@ -58,6 +61,27 @@ public final class StringSupport {
};
}
@ForceInline
public static String read(AbstractMemorySegmentImpl segment, long offset, Charset charset, long length) {
return readBytes(segment, offset, charset, length);
}
@ForceInline
public static String readBytes(AbstractMemorySegmentImpl segment, long offset, Charset charset, long length) {
if (length > Integer.MAX_VALUE) {
throw new IllegalArgumentException("Required length exceeds implementation limit");
}
final int lengthBytes = (int) length;
final byte[] bytes = new byte[lengthBytes];
MemorySegment.copy(segment, JAVA_BYTE, offset, bytes, 0, lengthBytes);
try {
return JAVA_LANG_ACCESS.uncheckedNewStringOrThrow(bytes, charset);
} catch (CharacterCodingException _) {
// use replacement characters for malformed input
return new String(bytes, charset);
}
}
@ForceInline
public static void write(AbstractMemorySegmentImpl segment, long offset, Charset charset, String string) {
switch (CharsetKind.of(charset)) {
@ -70,14 +94,7 @@ public final class StringSupport {
@ForceInline
private static String readByte(AbstractMemorySegmentImpl segment, long offset, Charset charset) {
final int len = strlenByte(segment, offset, segment.byteSize());
final byte[] bytes = new byte[len];
MemorySegment.copy(segment, JAVA_BYTE, offset, bytes, 0, len);
try {
return JAVA_LANG_ACCESS.uncheckedNewStringOrThrow(bytes, charset);
} catch (CharacterCodingException _) {
// use replacement characters for malformed input
return new String(bytes, charset);
}
return readBytes(segment, offset, charset, len);
}
@ForceInline
@ -89,14 +106,7 @@ public final class StringSupport {
@ForceInline
private static String readShort(AbstractMemorySegmentImpl segment, long offset, Charset charset) {
int len = strlenShort(segment, offset, segment.byteSize());
byte[] bytes = new byte[len];
MemorySegment.copy(segment, JAVA_BYTE, offset, bytes, 0, len);
try {
return JAVA_LANG_ACCESS.uncheckedNewStringOrThrow(bytes, charset);
} catch (CharacterCodingException _) {
// use replacement characters for malformed input
return new String(bytes, charset);
}
return readBytes(segment, offset, charset, len);
}
@ForceInline
@ -108,14 +118,7 @@ public final class StringSupport {
@ForceInline
private static String readInt(AbstractMemorySegmentImpl segment, long offset, Charset charset) {
int len = strlenInt(segment, offset, segment.byteSize());
byte[] bytes = new byte[len];
MemorySegment.copy(segment, JAVA_BYTE, offset, bytes, 0, len);
try {
return JAVA_LANG_ACCESS.uncheckedNewStringOrThrow(bytes, charset);
} catch (CharacterCodingException _) {
// use replacement characters for malformed input
return new String(bytes, charset);
}
return readBytes(segment, offset, charset, len);
}
@ForceInline
@ -345,22 +348,26 @@ public final class StringSupport {
}
}
public static boolean bytesCompatible(String string, Charset charset) {
return JAVA_LANG_ACCESS.bytesCompatible(string, charset);
public static boolean bytesCompatible(String string, Charset charset, int srcIndex, int numChars) {
return JAVA_LANG_ACCESS.bytesCompatible(string, charset, srcIndex, numChars);
}
public static int copyBytes(String string, MemorySegment segment, Charset charset, long offset) {
if (bytesCompatible(string, charset)) {
copyToSegmentRaw(string, segment, offset);
return string.length();
return copyBytes(string, segment, charset, offset, 0, string.length());
}
public static int copyBytes(String string, MemorySegment segment, Charset charset, long offset, int srcIndex, int numChars) {
if (bytesCompatible(string, charset, srcIndex, numChars)) {
copyToSegmentRaw(string, segment, offset, srcIndex, numChars);
return numChars;
} else {
byte[] bytes = string.getBytes(charset);
byte[] bytes = string.substring(srcIndex, srcIndex + numChars).getBytes(charset);
MemorySegment.copy(bytes, 0, segment, JAVA_BYTE, offset, bytes.length);
return bytes.length;
}
}
public static void copyToSegmentRaw(String string, MemorySegment segment, long offset) {
JAVA_LANG_ACCESS.copyToSegmentRaw(string, segment, offset);
public static void copyToSegmentRaw(String string, MemorySegment segment, long offset, int srcIndex, int srcLength) {
JAVA_LANG_ACCESS.copyToSegmentRaw(string, segment, offset, srcIndex, srcLength);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2025, 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
@ -68,7 +68,7 @@ public class ECDSAOperations {
public ECDSAOperations(ECOperations ecOps, ECPoint basePoint) {
this.ecOps = ecOps;
this.basePoint = toAffinePoint(basePoint, ecOps.getField());
this.basePoint = AffinePoint.fromECPoint(basePoint, ecOps.getField());
}
public ECOperations getEcOperations() {
@ -79,14 +79,6 @@ public class ECDSAOperations {
return ecOps.multiply(basePoint, scalar).asAffine();
}
public static AffinePoint toAffinePoint(ECPoint point,
IntegerFieldModuloP field) {
ImmutableIntegerModuloP affineX = field.getElement(point.getAffineX());
ImmutableIntegerModuloP affineY = field.getElement(point.getAffineY());
return new AffinePoint(affineX, affineY);
}
public static
Optional<ECDSAOperations> forParameters(ECParameterSpec ecParams) {
Optional<ECOperations> curveOps =

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 2026, 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,6 +36,8 @@ import javax.lang.model.type.DeclaredType;
* method. There is no guarantee that any particular annotation will
* always be represented by the same object.
*
* @jls 9.6 Annotation Interfaces
* @jls 9.7 Annotations
* @since 1.6
*/
public interface AnnotationMirror {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 2026, 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,14 +27,16 @@ package javax.lang.model.element;
/**
* Represents a value of an annotation interface element.
* A value is of one of the following types:
* <ul><li> a wrapper class (such as {@link Integer}) for a primitive type
* <li> {@code String}
* <li> {@code TypeMirror}
* <li> {@code VariableElement} (representing an enum constant)
* <li> {@code AnnotationMirror}
* A value is of one of the following types (JLS {@jls 9.6.1}):
* <ul><li> a {@linkplain java.lang##wrapperClass wrapper class} to hold a
* primitive type, such as an {@code Integer} object to hold an
* {@code int}
* <li> {@code String} representing a {@code String}
* <li> {@link javax.lang.model.type.TypeMirror TypeMirror} representing a {@code Class} literal
* <li> {@link VariableElement} representing an enum constant
* <li> {@link AnnotationMirror} representing an annotation
* <li> {@code List<? extends AnnotationValue>}
* (representing the elements, in declared order, if the value is an array)
* representing the elements, in declared order, if the value is an array
* </ul>
*
* @since 1.6

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 2026, 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
@ -120,6 +120,15 @@ public interface TypeMirror extends AnnotatedConstruct {
* The results of {@code t1.equals(t2)} and
* {@code Types.isSameType(t1, t2)} may differ.
*
* @apiNote The identity of a {@code TypeMirror} involves implicit
* state not directly accessible from its methods, including state
* about the presence of unrelated types. {@code TypeMirror}
* objects created by different implementations of these
* interfaces should <i>not</i> be expected to compare as equal
* even if &quot;the same&quot; type is being modeled; this is
* analogous to the inequality of {@code Class} objects for the
* same class file loaded through different class loaders.
*
* @param obj the object to be compared with this type
* @return {@code true} if the specified object is equal to this one
*/

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 2026, 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
@ -106,6 +106,15 @@ public interface Types {
* {@code TypeMirror} objects can have different annotations and
* still be considered the same.
*
* @apiNote The identity of a {@code TypeMirror} involves implicit
* state not directly accessible from its methods, including state
* about the presence of unrelated types. {@code TypeMirror}
* objects created by different implementations of these
* interfaces should <i>not</i> be expected to compare as equal
* even if &quot;the same&quot; type is being modeled; this is
* analogous to the inequality of {@code Class} objects for the
* same class file loaded through different class loaders.
*
* @param t1 the first type
* @param t2 the second type
* @return {@code true} if and only if the two types are the same

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2002, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2002, 2026, 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
@ -162,8 +162,7 @@ void DAUDIO_GetFormats(INT32 mixerIndex, INT32 deviceID, int isSource, void* cre
sampleRate, // sample rate
DAUDIO_PCM, // only accept PCM
bits == 8 ? FALSE : TRUE, // signed
bits == 8 ? FALSE // little-endian for 8bit
: UTIL_IsBigEndianPlatform());
FALSE); // all supported macOS versions run on LE
}
}
// add default format
@ -175,7 +174,7 @@ void DAUDIO_GetFormats(INT32 mixerIndex, INT32 deviceID, int isSource, void* cre
defSampleRate, // sample rate
DAUDIO_PCM, // PCM
TRUE, // signed
UTIL_IsBigEndianPlatform()); // native endianness
FALSE); // native endianness; all supported macOS versions run on LE
}
TRACE0("<<DAUDIO_GetFormats\n");

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2026, 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
@ -25,6 +25,7 @@
package com.sun.media.sound;
import java.nio.ByteOrder;
import java.util.StringTokenizer;
/**
@ -40,12 +41,6 @@ final class Platform {
private static boolean isNativeLibLoaded;
// SYSTEM CHARACTERISTICS
// vary according to hardware architecture
// intel is little-endian. sparc is big-endian.
private static boolean bigEndian;
static {
loadLibraries();
}
@ -66,7 +61,7 @@ final class Platform {
* Determine whether the system is big-endian.
*/
static boolean isBigEndian() {
return bigEndian;
return ByteOrder.nativeOrder().equals(ByteOrder.BIG_ENDIAN);
}
/**
@ -82,9 +77,6 @@ final class Platform {
if (Printer.err) Printer.err("Couldn't load library "+libName+": "+t.toString());
isNativeLibLoaded = false;
}
if (isNativeLibLoaded) {
bigEndian = nIsBigEndian();
}
}
static boolean isMidiIOEnabled() {
@ -98,7 +90,4 @@ final class Platform {
static boolean isDirectAudioEnabled() {
return isNativeLibLoaded;
}
// the following native method is implemented in Platform.c
private static native boolean nIsBigEndian();
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2026, 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
@ -1405,7 +1405,7 @@ public class BasicSplitPaneUI extends SplitPaneUI
// If the splitpane has a zero size then no op out of here.
// If we execute this function now, we're going to cause ourselves
// much grief.
if (containerSize.height <= 0 || containerSize.width <= 0 ) {
if (containerSize.height <= 0 && containerSize.width <= 0 ) {
lastSplitPaneSize = 0;
return;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2026, 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
@ -302,8 +302,16 @@ public class PNGImageDecoder extends ImageDecoder
int bitsPerPixel = samplesPerPixel*bitDepth;
int bytesPerPixel = (bitsPerPixel+7)>>3;
int pass, passLimit;
if(interlaceMethod==0) { pass = -1; passLimit = 0; }
else { pass = 0; passLimit = 7; }
boolean isDirectByteCopy;
if(interlaceMethod==0) {
pass = -1;
passLimit = 0;
isDirectByteCopy = bPixels != null && bitDepth == 8;
} else {
pass = 0;
passLimit = 7;
isDirectByteCopy = false;
}
while(++pass<=passLimit) {
int row = startingRow[pass];
int rowInc = rowIncrement[pass];
@ -334,7 +342,11 @@ public class PNGImageDecoder extends ImageDecoder
int spos=0;
int pixel = 0;
while (col < width) {
if(wPixels !=null) {
if (isDirectByteCopy) {
System.arraycopy(rowByteBuffer, spos, bPixels, col + rowOffset, width);
spos += width;
break;
} else if(wPixels !=null) {
switch(combinedType) {
case COLOR|ALPHA|(8<<3):
wPixels[col+rowOffset] =

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1995, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1995, 2026, 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
@ -92,8 +92,8 @@ public class XbmImageDecoder extends ImageDecoder {
byte[] raster = null;
IndexColorModel model = null;
String matchRegex = "(0[xX])?[0-9a-fA-F]+[\\s+]?[,|};]";
String replaceRegex = "(0[xX])|,|[\\s+]|[};]";
String matchRegex = "\\s*(0[xX])?((?:(?!,|\\};).)+)(,|\\};)";
String replaceRegex = "0[xX]|,|\\s+|\\};";
String line;
int lineNum = 0;
@ -111,11 +111,19 @@ public class XbmImageDecoder extends ImageDecoder {
}
try {
if (!token[2].isBlank() && state == 0) {
W = Integer.parseInt(token[2]);
state = 1; // after width is set
if (token[1].endsWith("th")) {
W = Integer.parseInt(token[2]);
} else if (token[1].endsWith("t")) {
H = Integer.parseInt(token[2]);
}
state = 1; // after first dimension is set
} else if (!token[2].isBlank() && state == 1) {
H = Integer.parseInt(token[2]);
state = 2; // after height is set
if (token[1].endsWith("th")) {
W = Integer.parseInt(token[2]);
} else if (token[1].endsWith("t")) {
H = Integer.parseInt(token[2]);
}
state = 2; // after second dimension is set
}
} catch (NumberFormatException nfe) {
// parseInt() can throw NFE
@ -147,59 +155,81 @@ public class XbmImageDecoder extends ImageDecoder {
error("Width or Height of XBM file not defined");
}
boolean contFlag = false;
StringBuilder sb = new StringBuilder();
// loop to process image data
while (!aborted && (line = br.readLine()) != null) {
lineNum++;
if (line.contains("[]")) {
Matcher matcher = Pattern.compile(matchRegex).matcher(line);
while (matcher.find()) {
if (y >= H) {
error("Scan size of XBM file exceeds"
+ " the defined width x height");
}
int startIndex = matcher.start();
int endIndex = matcher.end();
String hexByte = line.substring(startIndex, endIndex);
if (!(hexByte.startsWith("0x")
|| hexByte.startsWith("0X"))) {
error("Invalid hexadecimal number at Ln#:" + lineNum
+ " Col#:" + (startIndex + 1));
}
hexByte = hexByte.replaceAll(replaceRegex, "");
if (hexByte.length() != 2) {
error("Invalid hexadecimal number at Ln#:" + lineNum
+ " Col#:" + (startIndex + 1));
}
try {
n = Integer.parseInt(hexByte, 16);
} catch (NumberFormatException nfe) {
error("Error parsing hexadecimal at Ln#:" + lineNum
+ " Col#:" + (startIndex + 1));
}
for (int mask = 1; mask <= 0x80; mask <<= 1) {
if (x < W) {
if ((n & mask) != 0)
raster[x] = 1;
else
raster[x] = 0;
}
x++;
}
if (x >= W) {
int result = setPixels(0, y, W, 1, model, raster, 0, W);
if (result <= 0) {
error("Unexpected error occurred during setPixel()");
}
x = 0;
y++;
}
if (!contFlag) {
if (line.contains("[]")) {
contFlag = true;
} else {
continue;
}
}
int end = line.indexOf(';');
if (end >= 0) {
sb.append(line, 0, end + 1);
break;
} else {
sb.append(line).append(System.lineSeparator());
}
}
String resultLine = sb.toString();
int cutOffIndex = resultLine.indexOf('{');
resultLine = resultLine.substring(cutOffIndex + 1);
Matcher matcher = Pattern.compile(matchRegex).matcher(resultLine);
while (matcher.find()) {
if (y >= H) {
error("Scan size of XBM file exceeds"
+ " the defined width x height");
}
int startIndex = matcher.start();
int endIndex = matcher.end();
String hexByte = resultLine.substring(startIndex, endIndex);
hexByte = hexByte.replaceAll("^\\s+", "");
if (!(hexByte.startsWith("0x")
|| hexByte.startsWith("0X"))) {
error("Invalid hexadecimal number at Ln#:" + lineNum
+ " Col#:" + (startIndex + 1));
}
hexByte = hexByte.replaceAll(replaceRegex, "");
if (hexByte.length() != 2) {
error("Invalid hexadecimal number at Ln#:" + lineNum
+ " Col#:" + (startIndex + 1));
}
try {
n = Integer.parseInt(hexByte, 16);
} catch (NumberFormatException nfe) {
error("Error parsing hexadecimal at Ln#:" + lineNum
+ " Col#:" + (startIndex + 1));
}
for (int mask = 1; mask <= 0x80; mask <<= 1) {
if (x < W) {
if ((n & mask) != 0)
raster[x] = 1;
else
raster[x] = 0;
}
x++;
}
if (x >= W) {
int result = setPixels(0, y, W, 1, model, raster, 0, W);
if (result <= 0) {
error("Unexpected error occurred during setPixel()");
}
x = 0;
y++;
}
}
imageComplete(ImageConsumer.STATICIMAGEDONE, true);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1998, 2007, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1998, 2026, 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
@ -26,15 +26,6 @@
#include "Utilities.h"
int UTIL_IsBigEndianPlatform() {
#ifdef _LITTLE_ENDIAN
return 0;
#else
return 1;
#endif
}
void ThrowJavaMessageException(JNIEnv *e, const char *exClass, const char *msg) {
jclass newExcCls;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1998, 2026, 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
@ -28,10 +28,6 @@
#include "SoundDefs.h"
#include "Configure.h" // put flags for debug msgs etc. here
// return 1 if this platform is big endian, or 0 for little endian
int UTIL_IsBigEndianPlatform();
// ERROR PRINTS
#ifdef USE_ERROR
#define ERROR0(string) { fprintf(stdout, (string)); fflush(stdout); }

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2009, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2009, 2026, 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
@ -1403,6 +1403,7 @@ public class TypeAnnotations {
break;
}
}
scan(tree.dims);
scan(tree.elems);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2026, 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
@ -1107,6 +1107,7 @@ public class Annotate {
for (List<JCAnnotation> dimAnnos : tree.dimAnnotations)
enterTypeAnnotations(dimAnnos, env, sym, false);
scan(tree.elemtype);
scan(tree.dims);
scan(tree.elems);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 2026, 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
@ -50,6 +50,7 @@ import jdk.jpackage.internal.model.LinuxLauncher;
import jdk.jpackage.internal.model.LinuxPackage;
import jdk.jpackage.internal.model.Package;
import jdk.jpackage.internal.util.CompositeProxy;
import jdk.jpackage.internal.util.Enquoter;
import jdk.jpackage.internal.util.PathUtils;
import jdk.jpackage.internal.util.XmlUtils;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 2026, 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,13 +27,12 @@ package jdk.jpackage.internal;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Collection;
import java.util.Objects;
import java.util.Collections;
import java.util.Set;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.function.Predicate;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -48,9 +47,6 @@ public final class LibProvidersLookup {
return (new ToolValidator(TOOL_LDD).validate() == null);
}
public LibProvidersLookup() {
}
LibProvidersLookup setPackageLookup(PackageLookup v) {
packageLookup = v;
return this;
@ -87,23 +83,20 @@ public final class LibProvidersLookup {
}
private static List<Path> getNeededLibsForFile(Path path) throws IOException {
List<Path> result = new ArrayList<>();
int ret = Executor.of(TOOL_LDD, path.toString()).setOutputConsumer(lines -> {
lines.map(line -> {
Matcher matcher = LIB_IN_LDD_OUTPUT_REGEX.matcher(line);
if (matcher.find()) {
return matcher.group(1);
}
return null;
}).filter(Objects::nonNull).map(Path::of).forEach(result::add);
}).execute();
final var result = Executor.of(TOOL_LDD, path.toString()).saveOutput().execute();
if (ret != 0) {
if (result.getExitCode() != 0) {
// objdump failed. This is OK if the tool was applied to not a binary file
return Collections.emptyList();
}
return result;
return result.stdout().stream().map(line -> {
Matcher matcher = LIB_IN_LDD_OUTPUT_REGEX.matcher(line);
if (matcher.find()) {
return matcher.group(1);
}
return null;
}).filter(Objects::nonNull).map(Path::of).toList();
}
private static Collection<Path> getNeededLibsForFiles(List<Path> paths) {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2025, 2026, 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
@ -33,6 +33,7 @@ import static jdk.jpackage.internal.cli.StandardBundlingOperation.CREATE_LINUX_R
import java.util.Map;
import java.util.Optional;
import java.util.function.Supplier;
import java.util.stream.Stream;
import jdk.jpackage.internal.cli.Options;
import jdk.jpackage.internal.cli.StandardBundlingOperation;
@ -44,19 +45,34 @@ import jdk.jpackage.internal.util.Result;
public class LinuxBundlingEnvironment extends DefaultBundlingEnvironment {
public LinuxBundlingEnvironment() {
super(build()
.defaultOperation(() -> {
return LazyLoad.SYS_ENV.value().map(LinuxSystemEnvironment::nativePackageType).map(DESCRIPTORS::get);
})
.bundler(CREATE_LINUX_APP_IMAGE, LinuxBundlingEnvironment::createAppImage)
.bundler(CREATE_LINUX_DEB, LazyLoad::debSysEnv, LinuxBundlingEnvironment::createDebPackage)
.bundler(CREATE_LINUX_RPM, LazyLoad::rpmSysEnv, LinuxBundlingEnvironment::createRpmPackage));
super(build().mutate(builder -> {
// Wrap the generic Linux system environment supplier in the run-once wrapper
// as this supplier is called from both RPM and DEB Linux system environment suppliers.
var sysEnv = runOnce(() -> {
return LinuxSystemEnvironment.create();
});
Supplier<Result<LinuxDebSystemEnvironment>> debSysEnv = () -> {
return LinuxDebSystemEnvironment.create(sysEnv.get());
};
Supplier<Result<LinuxRpmSystemEnvironment>> rpmSysEnv = () -> {
return LinuxRpmSystemEnvironment.create(sysEnv.get());
};
builder.defaultOperation(() -> {
return sysEnv.get().value().map(LinuxSystemEnvironment::nativePackageType).map(DESCRIPTORS::get);
})
.bundler(CREATE_LINUX_DEB, debSysEnv, LinuxBundlingEnvironment::createDebPackage)
.bundler(CREATE_LINUX_RPM, rpmSysEnv, LinuxBundlingEnvironment::createRpmPackage);
}).bundler(CREATE_LINUX_APP_IMAGE, LinuxBundlingEnvironment::createAppImage));
}
private static void createDebPackage(Options options, LinuxDebSystemEnvironment sysEnv) {
createNativePackage(options,
LinuxFromOptions.createLinuxDebPackage(options),
LinuxFromOptions.createLinuxDebPackage(options, sysEnv),
buildEnv()::create,
LinuxBundlingEnvironment::buildPipeline,
(env, pkg, outputDir) -> {
@ -67,7 +83,7 @@ public class LinuxBundlingEnvironment extends DefaultBundlingEnvironment {
private static void createRpmPackage(Options options, LinuxRpmSystemEnvironment sysEnv) {
createNativePackage(options,
LinuxFromOptions.createLinuxRpmPackage(options),
LinuxFromOptions.createLinuxRpmPackage(options, sysEnv),
buildEnv()::create,
LinuxBundlingEnvironment::buildPipeline,
(env, pkg, outputDir) -> {
@ -90,23 +106,6 @@ public class LinuxBundlingEnvironment extends DefaultBundlingEnvironment {
return new BuildEnvFromOptions().predefinedAppImageLayout(APPLICATION_LAYOUT);
}
private static final class LazyLoad {
static Result<LinuxDebSystemEnvironment> debSysEnv() {
return DEB_SYS_ENV;
}
static Result<LinuxRpmSystemEnvironment> rpmSysEnv() {
return RPM_SYS_ENV;
}
private static final Result<LinuxSystemEnvironment> SYS_ENV = LinuxSystemEnvironment.create();
private static final Result<LinuxDebSystemEnvironment> DEB_SYS_ENV = LinuxDebSystemEnvironment.create(SYS_ENV);
private static final Result<LinuxRpmSystemEnvironment> RPM_SYS_ENV = LinuxRpmSystemEnvironment.create(SYS_ENV);
}
private static final Map<PackageType, BundlingOperationDescriptor> DESCRIPTORS = Stream.of(
CREATE_LINUX_DEB,
CREATE_LINUX_RPM

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2026, 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
@ -25,7 +25,6 @@
package jdk.jpackage.internal;
import static jdk.jpackage.internal.model.StandardPackageType.LINUX_DEB;
import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction;
import java.io.IOException;
@ -76,11 +75,11 @@ final class LinuxDebPackager extends LinuxPackager<LinuxDebPackage> {
try {
// Try the real path first as it works better on newer Ubuntu versions
return findProvidingPackages(realPath, sysEnv.dpkg());
return findProvidingPackages(realPath, sysEnv);
} catch (IOException ex) {
// Try the default path if differ
if (!realPath.equals(file)) {
return findProvidingPackages(file, sysEnv.dpkg());
return findProvidingPackages(file, sysEnv);
} else {
throw ex;
}
@ -107,7 +106,7 @@ final class LinuxDebPackager extends LinuxPackager<LinuxDebPackage> {
properties.forEach(property -> cmdline.add(property.name));
Map<String, String> actualValues = Executor.of(cmdline.toArray(String[]::new))
Map<String, String> actualValues = Executor.of(cmdline)
.saveOutput(true)
.executeExpectSuccess()
.getOutput().stream()
@ -158,9 +157,8 @@ final class LinuxDebPackager extends LinuxPackager<LinuxDebPackage> {
cmdline.addAll(List.of("-b", env.appImageDir().toString(), debFile.toAbsolutePath().toString()));
// run dpkg
RetryExecutor.retryOnKnownErrorMessage(
"semop(1): encountered an error: Invalid argument").execute(
cmdline.toArray(String[]::new));
Executor.of(cmdline).retryOnKnownErrorMessage(
"semop(1): encountered an error: Invalid argument").execute();
Log.verbose(I18N.format("message.output-to-location", debFile.toAbsolutePath()));
}
@ -233,7 +231,7 @@ final class LinuxDebPackager extends LinuxPackager<LinuxDebPackage> {
}
}
private static Stream<String> findProvidingPackages(Path file, Path dpkg) throws IOException {
private static Stream<String> findProvidingPackages(Path file, LinuxDebSystemEnvironment sysEnv) throws IOException {
//
// `dpkg -S` command does glob pattern lookup. If not the absolute path
// to the file is specified it might return mltiple package names.
@ -279,9 +277,9 @@ final class LinuxDebPackager extends LinuxPackager<LinuxDebPackage> {
Set<String> archPackages = new HashSet<>();
Set<String> otherPackages = new HashSet<>();
var debArch = LinuxPackageArch.getValue(LINUX_DEB);
var debArch = sysEnv.packageArch().value();
Executor.of(dpkg.toString(), "-S", file.toString())
Executor.of(sysEnv.dpkg().toString(), "-S", file.toString())
.saveOutput(true).executeExpectSuccess()
.getOutput().forEach(line -> {
Matcher matcher = PACKAGE_NAME_REGEX.matcher(line);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2025, 2026, 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
@ -28,7 +28,7 @@ import static jdk.jpackage.internal.LinuxSystemEnvironment.mixin;
import jdk.jpackage.internal.util.Result;
public interface LinuxDebSystemEnvironment extends LinuxSystemEnvironment, LinuxDebSystemEnvironmentMixin {
interface LinuxDebSystemEnvironment extends LinuxSystemEnvironment, LinuxDebSystemEnvironmentMixin {
static Result<LinuxDebSystemEnvironment> create(Result<LinuxSystemEnvironment> base) {
return mixin(LinuxDebSystemEnvironment.class, base, LinuxDebSystemEnvironmentMixin::create);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2025, 2026, 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
@ -29,7 +29,7 @@ import java.util.Objects;
import java.util.stream.Stream;
import jdk.jpackage.internal.util.Result;
public interface LinuxDebSystemEnvironmentMixin {
interface LinuxDebSystemEnvironmentMixin {
Path dpkg();
Path dpkgdeb();
Path fakeroot();

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2025, 2026, 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
@ -70,9 +70,9 @@ final class LinuxFromOptions {
return LinuxApplication.create(appBuilder.create());
}
static LinuxRpmPackage createLinuxRpmPackage(Options options) {
static LinuxRpmPackage createLinuxRpmPackage(Options options, LinuxRpmSystemEnvironment sysEnv) {
final var superPkgBuilder = createLinuxPackageBuilder(options, LINUX_RPM);
final var superPkgBuilder = createLinuxPackageBuilder(options, sysEnv, LINUX_RPM);
final var pkgBuilder = new LinuxRpmPackageBuilder(superPkgBuilder);
@ -81,9 +81,9 @@ final class LinuxFromOptions {
return pkgBuilder.create();
}
static LinuxDebPackage createLinuxDebPackage(Options options) {
static LinuxDebPackage createLinuxDebPackage(Options options, LinuxDebSystemEnvironment sysEnv) {
final var superPkgBuilder = createLinuxPackageBuilder(options, LINUX_DEB);
final var superPkgBuilder = createLinuxPackageBuilder(options, sysEnv, LINUX_DEB);
final var pkgBuilder = new LinuxDebPackageBuilder(superPkgBuilder);
@ -99,7 +99,7 @@ final class LinuxFromOptions {
return pkg;
}
private static LinuxPackageBuilder createLinuxPackageBuilder(Options options, StandardPackageType type) {
private static LinuxPackageBuilder createLinuxPackageBuilder(Options options, LinuxSystemEnvironment sysEnv, StandardPackageType type) {
final var app = createLinuxApplication(options);
@ -107,6 +107,8 @@ final class LinuxFromOptions {
final var pkgBuilder = new LinuxPackageBuilder(superPkgBuilder);
pkgBuilder.arch(sysEnv.packageArch());
LINUX_PACKAGE_DEPENDENCIES.ifPresentIn(options, pkgBuilder::additionalDependencies);
LINUX_APP_CATEGORY.ifPresentIn(options, pkgBuilder::category);
LINUX_MENU_GROUP.ifPresentIn(options, pkgBuilder::menuGroupName);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2022, 2026, 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
@ -32,6 +32,7 @@ import java.util.List;
import java.util.Map;
import jdk.jpackage.internal.model.Launcher;
import jdk.jpackage.internal.model.Package;
import jdk.jpackage.internal.util.Enquoter;
/**
* Helper to install launchers as services using "systemd".

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2025, 2026, 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
@ -25,18 +25,20 @@
package jdk.jpackage.internal;
import java.io.IOException;
import static jdk.jpackage.internal.util.function.ThrowingSupplier.toSupplier;
import java.util.ArrayList;
import jdk.jpackage.internal.model.StandardPackageType;
import jdk.jpackage.internal.util.CommandOutputControl;
import jdk.jpackage.internal.util.Result;
final class LinuxPackageArch {
record LinuxPackageArch(String value) {
static String getValue(StandardPackageType pkgType) {
static Result<LinuxPackageArch> create(StandardPackageType pkgType) {
switch (pkgType) {
case LINUX_RPM -> {
return RpmPackageArch.VALUE;
return rpm().map(LinuxPackageArch::new);
}
case LINUX_DEB -> {
return DebPackageArch.VALUE;
return deb().map(LinuxPackageArch::new);
}
default -> {
throw new IllegalArgumentException();
@ -44,62 +46,51 @@ final class LinuxPackageArch {
}
}
private static class DebPackageArch {
static final String VALUE = toSupplier(DebPackageArch::getValue).get();
private static String getValue() throws IOException {
return Executor.of("dpkg", "--print-architecture").saveOutput(true)
.executeExpectSuccess().getOutput().get(0);
}
private static Result<String> deb() {
var exec = Executor.of("dpkg", "--print-architecture").saveOutput(true);
return Result.of(exec::executeExpectSuccess, IOException.class)
.flatMap(LinuxPackageArch::getStdoutFirstLine);
}
private static class RpmPackageArch {
/*
* Various ways to get rpm arch. Needed to address JDK-8233143. rpmbuild is mandatory for
* rpm packaging, try it first. rpm is optional and may not be available, use as the last
* resort.
*/
private static enum RpmArchReader {
Rpmbuild("rpmbuild", "--eval=%{_target_cpu}"),
Rpm("rpm", "--eval=%{_target_cpu}");
RpmArchReader(String... cmdline) {
this.cmdline = cmdline;
private static Result<String> rpm() {
var errors = new ArrayList<Exception>();
for (var tool : RpmArchReader.values()) {
var result = tool.getRpmArch();
if (result.hasValue()) {
return result;
} else {
errors.addAll(result.errors());
}
String getRpmArch() throws IOException {
Executor exec = Executor.of(cmdline).saveOutput(true);
switch (this) {
case Rpm -> {
exec.executeExpectSuccess();
}
case Rpmbuild -> {
if (exec.execute() != 0) {
return null;
}
}
default -> {
throw new UnsupportedOperationException();
}
}
return exec.getOutput().get(0);
}
private final String[] cmdline;
}
static final String VALUE = toSupplier(RpmPackageArch::getValue).get();
return Result.ofErrors(errors);
}
private static String getValue() throws IOException {
for (var rpmArchReader : RpmArchReader.values()) {
var rpmArchStr = rpmArchReader.getRpmArch();
if (rpmArchStr != null) {
return rpmArchStr;
}
}
throw new RuntimeException("error.rpm-arch-not-detected");
/*
* Various ways to get rpm arch. Needed to address JDK-8233143. rpmbuild is mandatory for
* rpm packaging, try it first. rpm is optional and may not be available, use as the last
* resort.
*/
private enum RpmArchReader {
RPMBUILD("rpmbuild", "--eval=%{_target_cpu}"),
RPM("rpm", "--eval=%{_target_cpu}");
RpmArchReader(String... cmdline) {
this.cmdline = cmdline;
}
Result<String> getRpmArch() {
var exec = Executor.of(cmdline).saveOutput(true);
return Result.of(exec::executeExpectSuccess, IOException.class)
.flatMap(LinuxPackageArch::getStdoutFirstLine);
}
private final String[] cmdline;
}
private static Result<String> getStdoutFirstLine(CommandOutputControl.Result result) {
return Result.of(() -> {
return result.stdout().stream().findFirst().orElseThrow(result::unexpected);
}, IOException.class);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2025, 2026, 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
@ -83,7 +83,7 @@ final class LinuxPackageBuilder {
category(),
Optional.ofNullable(additionalDependencies),
release(),
pkg.asStandardPackageType().map(LinuxPackageArch::getValue).orElseThrow()));
arch.value()));
}
LinuxPackageBuilder literalName(String v) {
@ -119,6 +119,11 @@ final class LinuxPackageBuilder {
return Optional.ofNullable(release);
}
LinuxPackageBuilder arch(LinuxPackageArch v) {
arch = v;
return this;
}
private static LinuxApplicationLayout usrTreePackageLayout(Path prefix, String packageName) {
final var lib = prefix.resolve(Path.of("lib", packageName));
return LinuxApplicationLayout.create(
@ -184,6 +189,7 @@ final class LinuxPackageBuilder {
private String category;
private String additionalDependencies;
private String release;
private LinuxPackageArch arch;
private final PackageBuilder pkgBuilder;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2025, 2026, 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
@ -28,7 +28,7 @@ import static jdk.jpackage.internal.LinuxSystemEnvironment.mixin;
import jdk.jpackage.internal.util.Result;
public interface LinuxRpmSystemEnvironment extends LinuxSystemEnvironment, LinuxRpmSystemEnvironmentMixin {
interface LinuxRpmSystemEnvironment extends LinuxSystemEnvironment, LinuxRpmSystemEnvironmentMixin {
static Result<LinuxRpmSystemEnvironment> create(Result<LinuxSystemEnvironment> base) {
return mixin(LinuxRpmSystemEnvironment.class, base, LinuxRpmSystemEnvironmentMixin::create);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2025, 2026, 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
@ -32,7 +32,7 @@ import java.util.stream.Stream;
import jdk.jpackage.internal.model.DottedVersion;
import jdk.jpackage.internal.util.Result;
public interface LinuxRpmSystemEnvironmentMixin {
interface LinuxRpmSystemEnvironmentMixin {
Path rpm();
Path rpmbuild();

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2025, 2026, 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 @@ package jdk.jpackage.internal;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Supplier;
import jdk.jpackage.internal.model.PackageType;
@ -35,9 +34,10 @@ import jdk.jpackage.internal.model.StandardPackageType;
import jdk.jpackage.internal.util.CompositeProxy;
import jdk.jpackage.internal.util.Result;
public interface LinuxSystemEnvironment extends SystemEnvironment {
interface LinuxSystemEnvironment extends SystemEnvironment {
boolean soLookupAvailable();
PackageType nativePackageType();
LinuxPackageArch packageArch();
static Result<LinuxSystemEnvironment> create() {
return detectNativePackageType().map(LinuxSystemEnvironment::create).orElseGet(() -> {
@ -45,7 +45,7 @@ public interface LinuxSystemEnvironment extends SystemEnvironment {
});
}
static Optional<PackageType> detectNativePackageType() {
static Optional<StandardPackageType> detectNativePackageType() {
if (Internal.isDebian()) {
return Optional.of(StandardPackageType.LINUX_DEB);
} else if (Internal.isRpm()) {
@ -55,13 +55,14 @@ public interface LinuxSystemEnvironment extends SystemEnvironment {
}
}
static Result<LinuxSystemEnvironment> create(PackageType nativePackageType) {
return Result.ofValue(new Stub(LibProvidersLookup.supported(),
Objects.requireNonNull(nativePackageType)));
static Result<LinuxSystemEnvironment> create(StandardPackageType nativePackageType) {
return LinuxPackageArch.create(nativePackageType).map(arch -> {
return new Stub(LibProvidersLookup.supported(), nativePackageType, arch);
});
}
static <T, U extends LinuxSystemEnvironment> U createWithMixin(Class<U> type, LinuxSystemEnvironment base, T mixin) {
return CompositeProxy.create(type, base, mixin);
return CompositeProxy.build().invokeTunnel(CompositeProxyTunnel.INSTANCE).create(type, base, mixin);
}
static <T, U extends LinuxSystemEnvironment> Result<U> mixin(Class<U> type,
@ -79,7 +80,7 @@ public interface LinuxSystemEnvironment extends SystemEnvironment {
}
}
record Stub(boolean soLookupAvailable, PackageType nativePackageType) implements LinuxSystemEnvironment {
record Stub(boolean soLookupAvailable, PackageType nativePackageType, LinuxPackageArch packageArch) implements LinuxSystemEnvironment {
}
static final class Internal {

Some files were not shown because too many files have changed in this diff Show More