Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 27 additions & 14 deletions test/jdk/java/lang/ref/FinalizerHistogramTest.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 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
Expand All @@ -21,8 +21,9 @@
* questions.
*/

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import sun.hotspot.WhiteBox;

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;

import java.lang.reflect.Method;
Expand All @@ -32,37 +33,49 @@
* @test
* @summary Unit test for FinalizerHistogram
* @modules java.base/java.lang.ref:open
* @run main FinalizerHistogramTest
* @library /test/lib
* @build sun.hotspot.WhiteBox
* @run driver ClassFileInstaller sun.hotspot.WhiteBox
* @run main/othervm
* -Xbootclasspath/a:.
* -XX:+UnlockDiagnosticVMOptions
* -XX:+WhiteBoxAPI
* FinalizerHistogramTest
*/

public class FinalizerHistogramTest {
static ReentrantLock lock = new ReentrantLock();
static volatile int wasInitialized = 0;
static volatile int wasTrapped = 0;
static final int objectsCount = 1000;
static final AtomicInteger initializedCount = new AtomicInteger(0);
static final int OBJECTS_COUNT = 1000;

static WhiteBox wb;

static class MyObject {
public MyObject() {
// Make sure object allocation/deallocation is not optimized out
wasInitialized += 1;
initializedCount.incrementAndGet();
}

protected void finalize() {
// Trap the object in a finalization queue
wasTrapped += 1;
lock.lock();
}
}

public static void main(String[] argvs) {
public static void main(String[] argvs) throws InterruptedException {
try {
lock.lock();
for(int i = 0; i < objectsCount; ++i) {
for(int i = 0; i < OBJECTS_COUNT; ++i) {
new MyObject();
}
System.out.println("Objects intialized: " + objectsCount);
System.gc();
while(wasTrapped < 1);
System.out.println("Objects intialized: " + initializedCount.get());
wb = WhiteBox.getWhiteBox();
wb.fullGC();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the change from System.gc to WB.fullGC?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use WB.fullGC instead of System.gc is a good practice sometimes, this will make test more robustness and stable, such as openjdk/jdk#29729.

System.gc() is only a hint to the JVM — it does not guarantee that a full collection runs, that all unreachable objects are collected, or that reference/finalizer processing has completed. In this test that was the root cause of the intermittent failure: after System.gc(), the old code spun on while (wasTrapped < 1), i.e. it proceeded as soon as the first MyObject.finalize() was entered. At that moment other MyObject instances might not yet have been discovered and enqueued for finalization, so FinalizerHistogram could legitimately show no MyObject entries.

WhiteBox.fullGC() (see WB_FullGC in whitebox.cpp) forces a full heap collection with GCCause::_wb_full_gc and clears soft references, so unreachable MyObject instances are actually collected. The follow-up loop on wb.waitForReferenceProcessing() (which delegates to Reference.waitForReferenceProcessing()) then waits until pending reference processing — including work handed off to the finalizer thread — has quiesced (false means no active/pending processing). Only after that do we read the histogram, which matches the test's intent: at least one object blocked in finalize(), with others still waiting in the finalizer queue.

So the change is not “use WhiteBox for fun”; it replaces a racy System.gc() + busy-wait with deterministic GC + reference-processing synchronization, which is the actual fix for JDK-8298783.

boolean refProResult;
do {
refProResult = wb.waitForReferenceProcessing();
System.out.println("waitForReferenceProcessing returned: " + refProResult);
} while (refProResult);

Class<?> klass = Class.forName("java.lang.ref.FinalizerHistogram");

Expand Down