Add BasicPhantomReference for debugging
This commit is contained in:
+172
@@ -1,9 +1,17 @@
|
||||
package com.seibel.distanthorizons.core.util.objects.pooling;
|
||||
|
||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.logging.f3.F3Screen;
|
||||
import com.seibel.distanthorizons.core.util.ThreadUtil;
|
||||
import com.seibel.distanthorizons.core.util.objects.Pair;
|
||||
|
||||
import java.lang.ref.PhantomReference;
|
||||
import java.lang.ref.Reference;
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class PhantomLoggingHelper
|
||||
@@ -57,4 +65,168 @@ public class PhantomLoggingHelper
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// helper classes //
|
||||
//================//
|
||||
//region
|
||||
|
||||
/**
|
||||
* Can be quickly added to a {@link AutoCloseable} implementing
|
||||
* class to confirm it's being properly closed.
|
||||
*/
|
||||
public static class BasicPhantomReference implements AutoCloseable
|
||||
{
|
||||
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
||||
|
||||
/** if enabled the number of GC'ed buffers will be logged */
|
||||
private static final boolean LOG_PHANTOM_RECOVERY = true;
|
||||
/**
|
||||
* If enabled the GC'ed buffers allocation/upload stacks will be logged.
|
||||
* Note: due to how the buffers are often run on the render thread,
|
||||
* these stacks will likely only be of limited use.
|
||||
* For more robust debugging it would likely be best to somehow track
|
||||
* the stacks of where these calls are happening before they're queued
|
||||
* for the render thread.
|
||||
*/
|
||||
private static final boolean LOG_PHANTOM_ALLOCATION_STACKS = true;
|
||||
|
||||
private static final int PHANTOM_REF_CHECK_TIME_IN_MS = 5 * 1000;
|
||||
private static final ReferenceQueue<BasicPhantomReference> PHANTOM_REFERENCE_QUEUE = new ReferenceQueue<>();
|
||||
private static final ConcurrentHashMap<PhantomReference<? extends BasicPhantomReference>, Class<?>> PHANTOM_TO_PARENT_CLASS = new ConcurrentHashMap<>();
|
||||
|
||||
private static final ThreadPoolExecutor CLEANUP_THREAD = ThreadUtil.makeSingleDaemonThreadPool("BasicPhantom Cleanup");
|
||||
|
||||
|
||||
private final Class<?> parentClass;
|
||||
private final PhantomReference<? extends BasicPhantomReference> phantomReference;
|
||||
|
||||
|
||||
|
||||
//==============//
|
||||
// constructors //
|
||||
//==============//
|
||||
//region
|
||||
|
||||
static { CLEANUP_THREAD.execute(() -> runPhantomReferenceCleanupLoop()); }
|
||||
|
||||
public BasicPhantomReference(Class<?> parentClass)
|
||||
{
|
||||
this.parentClass = parentClass;
|
||||
this.phantomReference = new PhantomReference<>(this, PHANTOM_REFERENCE_QUEUE);
|
||||
PHANTOM_TO_PARENT_CLASS.put(this.phantomReference, this.parentClass);
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// base overrides //
|
||||
//================//
|
||||
//region
|
||||
|
||||
@Override
|
||||
public void close()
|
||||
{
|
||||
this.phantomReference.clear();
|
||||
PHANTOM_TO_PARENT_CLASS.remove(this.phantomReference);
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// static cleanup //
|
||||
//================//
|
||||
//region
|
||||
|
||||
private static void runPhantomReferenceCleanupLoop()
|
||||
{
|
||||
// these arrays are stored here so they don't have to be re-allocated each loop
|
||||
ArrayList<Pair<String, AtomicInteger>> allocationStackTraceCountPairList = new ArrayList<>();
|
||||
ArrayList<Pair<String, AtomicInteger>> parentClassNameCountPairList = new ArrayList<>();
|
||||
|
||||
while (true)
|
||||
{
|
||||
allocationStackTraceCountPairList.clear();
|
||||
parentClassNameCountPairList.clear();
|
||||
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
Thread.sleep(PHANTOM_REF_CHECK_TIME_IN_MS);
|
||||
}
|
||||
catch (InterruptedException ignore) { }
|
||||
|
||||
int collectedCount = 0;
|
||||
|
||||
Reference<? extends BasicPhantomReference> phantomRef = PHANTOM_REFERENCE_QUEUE.poll();
|
||||
while (phantomRef != null)
|
||||
{
|
||||
// destroy the buffer if it hasn't been cleared yet
|
||||
Class<?> parentClass = PHANTOM_TO_PARENT_CLASS.remove((PhantomReference<? extends BasicPhantomReference>)phantomRef); // cast to make IntelliJ happy
|
||||
{
|
||||
String parentClassName = "NULL";
|
||||
if (parentClass != null)
|
||||
{
|
||||
parentClassName = parentClass.getSimpleName();
|
||||
}
|
||||
|
||||
PhantomLoggingHelper.putAndIncrementTrackingString(parentClassName, parentClassNameCountPairList);
|
||||
//LOGGER.info("Phantom collected for class: [" + parentClassName + "]");
|
||||
}
|
||||
|
||||
|
||||
//if (LOG_PHANTOM_ALLOCATION_STACKS) // stack trace shouldn't be null, but just in case
|
||||
//{
|
||||
// String stack = BUFFER_ID_TO_ALLOCATION_STRING.get(idRef);
|
||||
// if (stack != null)
|
||||
// {
|
||||
// PhantomLoggingHelper.putAndIncrementTrackingString(stack, allocationStackTraceCountPairList);
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
collectedCount++;
|
||||
phantomRef = PHANTOM_REFERENCE_QUEUE.poll();
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (LOG_PHANTOM_RECOVERY)
|
||||
{
|
||||
// we only want to log when something has been returned
|
||||
if (collectedCount != 0)
|
||||
{
|
||||
LOGGER.warn("Phantoms collected: ["+ F3Screen.NUMBER_FORMAT.format(collectedCount)+"].");
|
||||
|
||||
PhantomLoggingHelper.LogAllocationStackTracePairCounts(LOGGER, parentClassNameCountPairList);
|
||||
|
||||
//// log stack traces if present
|
||||
//if (LOG_PHANTOM_ALLOCATION_STACKS)
|
||||
//{
|
||||
// PhantomLoggingHelper.LogAllocationStackTracePairCounts(LOGGER, allocationStackTraceCountPairList);
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOGGER.error("Unexpected error in buffer cleanup thread: [" + e.getMessage() + "].", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user