Skip to content
Open
Show file tree
Hide file tree
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
59 changes: 45 additions & 14 deletions core/src/main/java/lucee/commons/lang/PhysicalClassLoader.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@
import java.lang.instrument.UnmodifiableClassException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import lucee.commons.digest.HashUtil;
Expand Down Expand Up @@ -56,6 +58,17 @@ public final class PhysicalClassLoader extends URLClassLoader implements Extenda
private static final double CLASSLOADER_INSPECTION_COUNT = Caster.toIntValue(SystemUtil.getSystemPropOrEnvVar("lucee.template.classloader.inspection.count", null), 1000);
private static final double CLASSLOADER_INSPECTION_RATIO = Caster.toIntValue(SystemUtil.getSystemPropOrEnvVar("lucee.template.classloader.inspection.ratio", null), 3);

// Track last flush stats for testing/debugging
private static volatile int lastFlushPagesCleared = 0;

public static int getLastFlushPagesCleared() {
return lastFlushPagesCleared;
}

public static void resetLastFlushPagesCleared() {
lastFlushPagesCleared = 0;
}

static {
boolean res = registerAsParallelCapable();
}
Expand All @@ -69,19 +82,25 @@ public final class PhysicalClassLoader extends URLClassLoader implements Extenda
private Map<String, Integer> allLoadedClasses = new ConcurrentHashMap<>(); // this includes all renames
private Map<String, String> unavaiClasses = new ConcurrentHashMap<>();

private PageSourcePool pageSourcePool;
private final Set<PageSourcePool> pageSourcePools = Collections.newSetFromMap(new ConcurrentHashMap<>());

public void registerPageSourcePool( PageSourcePool pool ) {
if ( pool != null ) {
pageSourcePools.add( pool );
}
}

private boolean rpc;

private String birthplace;

public final String id;

PhysicalClassLoader(Config c, List<Resource> resources, Resource directory, ClassLoader parentClassLoader, ClassLoader addionalClassLoader, PageSourcePool pageSourcePool,
PhysicalClassLoader(Config c, List<Resource> resources, Resource directory, ClassLoader parentClassLoader, ClassLoader addionalClassLoader,
boolean rpc) throws IOException {

this(c, PhysicalClassLoaderFactory.doURLs(resources), resources, directory,
parentClassLoader == null ? (parentClassLoader = SystemUtil.getCombinedClassLoader()) : parentClassLoader, addionalClassLoader, pageSourcePool, rpc);
parentClassLoader == null ? (parentClassLoader = SystemUtil.getCombinedClassLoader()) : parentClassLoader, addionalClassLoader, rpc);

// check directory
if (!directory.exists()) directory.mkdirs();
Expand All @@ -90,13 +109,12 @@ public final class PhysicalClassLoader extends URLClassLoader implements Extenda
}

private PhysicalClassLoader(Config c, URL[] urls, List<Resource> resources, Resource directory, ClassLoader parentClassLoader, ClassLoader addionalClassLoader,
PageSourcePool pageSourcePool, boolean rpc) {
boolean rpc) {
super(urls, parentClassLoader == null ? (parentClassLoader = SystemUtil.getCombinedClassLoader()) : parentClassLoader);
this.resources = resources;
config = (ConfigPro) c;
this.addionalClassLoader = addionalClassLoader;
this.birthplace = ExceptionUtil.getStacktrace(new Throwable(), false);
this.pageSourcePool = pageSourcePool;

this.directory = directory;
this.rpc = rpc;
Expand All @@ -112,9 +130,15 @@ private PhysicalClassLoader(Config c, URL[] urls, List<Resource> resources, Reso
}

public static PhysicalClassLoader flush(PhysicalClassLoader existing, Config config) {
if (existing.pageSourcePool != null) existing.pageSourcePool.clearPages(existing);
int pagesCleared = 0;
for (PageSourcePool pool : existing.pageSourcePools) {
pagesCleared += pool.clearPages(existing);
}
lastFlushPagesCleared = pagesCleared;
PhysicalClassLoader clone = new PhysicalClassLoader(config, existing.getURLs(), existing.resources, existing.directory, existing.getParent(), existing.addionalClassLoader,
null, existing.rpc);
existing.rpc);
// copy registered pools to the new classloader
clone.pageSourcePools.addAll(existing.pageSourcePools);
DynamicInvoker instance = DynamicInvoker.getExistingInstance();
int count = 0;
if (instance != null) count += instance.remove(existing);
Expand All @@ -125,17 +149,17 @@ public static PhysicalClassLoader flush(PhysicalClassLoader existing, Config con
for (Integer i: existing.allLoadedClasses.values()) {
allClassesBytes += i.intValue();
}
LogUtil.log(Log.LEVEL_INFO, "physical-classloader",
"flush physical classloader [" + existing.getDirectory() + "] because we reached the size limit (all loaded classes count/size: " + all + "/"
+ StringUtil.byteFormat(allClassesBytes) + "; unique loaded classes: " + unique + "; ratio: " + (all / unique) + "), removed " + count
+ " cache elements from dynamic invoker");
int level = (pagesCleared > 0 || count > 0) ? Log.LEVEL_INFO : Log.LEVEL_DEBUG;
LogUtil.log(level, "physical-classloader",
"flush physical classloader [" + existing.getDirectory() + "] (classes: " + all + "/" + unique + ", " + StringUtil.byteFormat(allClassesBytes)
+ ", pages cleared: " + pagesCleared + ", dynamic invoker: " + count + ")");
return clone;
}

public static PhysicalClassLoader flushIfNecessary(PhysicalClassLoader existing, Config config) {
double all;

if (LogUtil.does(Log.LEVEL_DEBUG)) {
if (LogUtil.does(Log.LEVEL_TRACE)) {
int allClasses = existing.allLoadedClasses.size();
int allClassesBytes = 0;
int uniqueClasses = existing.loadedClasses.size();
Expand All @@ -145,7 +169,10 @@ public static PhysicalClassLoader flushIfNecessary(PhysicalClassLoader existing,
allClassesBytes += i.intValue();
}

LogUtil.log(Log.LEVEL_DEBUG, "physical-classloader",
boolean willFlush = allClasses > CLASSLOADER_INSPECTION_SIZE && ratio > CLASSLOADER_INSPECTION_RATIO;
int level = willFlush ? Log.LEVEL_DEBUG : Log.LEVEL_TRACE;

LogUtil.log(level, "physical-classloader",
"checking if flush necessary for physical classloader [" + existing.getDirectory() + "]: " + "all loaded classes: " + allClasses + " ("
+ StringUtil.byteFormat(allClassesBytes) + "), " + "unique loaded classes: " + uniqueClasses + ", " + "ratio: " + String.format("%.2f", ratio) + ", "
+ "inspection size threshold: " + Caster.toString(CLASSLOADER_INSPECTION_COUNT) + "/" + Caster.toString(CLASSLOADER_INSPECTION_SIZE) + ", "
Expand Down Expand Up @@ -439,7 +466,11 @@ private void clear() {
}

private void clear(boolean clearPagePool) {
if (clearPagePool && pageSourcePool != null) pageSourcePool.clearPages(this);
if (clearPagePool) {
for (PageSourcePool pool : pageSourcePools) {
pool.clearPages(this);
}
}
this.loadedClasses.clear();
this.allLoadedClasses.clear();
this.unavaiClasses.clear();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public static PhysicalClassLoader getPhysicalClassLoader(Config c, Resource dire
PhysicalClassLoader existing = classLoaders.get(key);
if (existing != null) PhysicalClassLoader.flush(existing, c);
}
classLoaders.put(key, rpccl = new PhysicalClassLoader(c, new ArrayList<Resource>(), directory, SystemUtil.getCombinedClassLoader(), null, null, false));
classLoaders.put(key, rpccl = new PhysicalClassLoader(c, new ArrayList<Resource>(), directory, SystemUtil.getCombinedClassLoader(), null, false));
return rpccl;
}
}
Expand All @@ -70,9 +70,16 @@ public static PhysicalClassLoader getPhysicalClassLoader(Config c, Resource dire

public static PhysicalClassLoader getRPCClassLoader(Config c, JavaSettings js, boolean reload, ClassLoader parent) throws IOException {
String key = js == null ? "orphan" : ((JavaSettingsImpl) js).id();
String parentInfo = "null";
if (parent != null) {
if (parent instanceof PhysicalClassLoader) key += "_" + ((PhysicalClassLoader) parent).id;
else key += "_" + parent.hashCode();
if (parent instanceof PhysicalClassLoader) {
key += "_" + ((PhysicalClassLoader) parent).id;
parentInfo = "PhysicalClassLoader[id=" + ((PhysicalClassLoader) parent).id + "]";
}
else {
key += "_" + parent.hashCode();
parentInfo = parent.getClass().getName() + "@" + parent.hashCode();
}
}
PhysicalClassLoader rpccl = reload ? null : classLoaders.get(key);

Expand All @@ -93,14 +100,15 @@ public static PhysicalClassLoader getRPCClassLoader(Config c, JavaSettings js, b
resources = toSortedList(((JavaSettingsImpl) js).getAllResources());
}
Resource dir = storeResourceMeta(c, key, js, resources);
// (Config config, String key, JavaSettings js, Collection<Resource> _resources)
classLoaders.put(key, rpccl = new PhysicalClassLoader(c, resources, dir, parent != null ? parent : SystemUtil.getCombinedClassLoader(), null, null, true));
lucee.aprint.o( "PhysicalClassLoaderFactory.getRPCClassLoader: Creating new RPC classloader: key=[" + key + "], parent=[" + parentInfo + "], jsId=[" + (js == null ? "null" : ((JavaSettingsImpl) js).id()) + "], totalClassLoaders=" + (classLoaders.size() + 1) );
classLoaders.put(key, rpccl = new PhysicalClassLoader(c, resources, dir, parent != null ? parent : SystemUtil.getCombinedClassLoader(), null, true));
return rpccl;
}
}
}

// at this point we know we had an existing one
lucee.aprint.o( "PhysicalClassLoaderFactory.getRPCClassLoader: Reusing existing RPC classloader: key=[" + key + "], parent=[" + parentInfo + "], pclId=[" + rpccl.id + "], totalClassLoaders=" + classLoaders.size() );
PhysicalClassLoader flushed = PhysicalClassLoader.flushIfNecessary(rpccl, c);
if (flushed != null) {
classLoaders.put(key, rpccl = flushed);
Expand All @@ -122,8 +130,7 @@ public static PhysicalClassLoader getRPCClassLoader(Config c, BundleClassLoader
}
Resource dir = c.getClassDirectory().getRealResource("RPC/" + key);
if (!dir.exists()) ResourceUtil.createDirectoryEL(dir, true);
// (Config config, String key, JavaSettings js, Collection<Resource> _resources)
classLoaders.put(key, rpccl = new PhysicalClassLoader(c, new ArrayList<Resource>(), dir, SystemUtil.getCombinedClassLoader(), bcl, null, true));
classLoaders.put(key, rpccl = new PhysicalClassLoader(c, new ArrayList<Resource>(), dir, SystemUtil.getCombinedClassLoader(), bcl, true));
return rpccl;
}
}
Expand Down
2 changes: 2 additions & 0 deletions core/src/main/java/lucee/runtime/MappingImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,7 @@ public Class<?> loadClass(String className) {

private Class<?> loadClass(String className, byte[] code) throws IOException, ClassNotFoundException {
PhysicalClassLoader pcl = PhysicalClassLoaderFactory.getPhysicalClassLoader(config, getClassRootDirectory(), false);
pcl.registerPageSourcePool( pageSourcePool );
/*
* PhysicalClassLoaderReference pclr = loaders.get(className); PhysicalClassLoader pcl = pclr ==
* null ? null : pclr.get(); if (pcl == null || code != null) {// || pcl.getSize(true) > 3 if (pcl
Expand All @@ -280,6 +281,7 @@ private Class<?> loadClass(String className, byte[] code) throws IOException, Cl
}
catch (UnmodifiableClassException e) {
pcl = PhysicalClassLoaderFactory.getPhysicalClassLoader(config, getClassRootDirectory(), true);
pcl.registerPageSourcePool( pageSourcePool );
try {
return pcl.loadClass(className, code);
}
Expand Down
12 changes: 7 additions & 5 deletions core/src/main/java/lucee/runtime/PageSourceImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ && isLoad(LOAD_PHYSICAL))
// synchronized (SystemUtil.createToken("PageSource", getRealpathWithVirtual())) {
// new class
if (flush || !classFile.exists()) {
LogUtil.log(config, Log.LEVEL_DEBUG, "compile", "compile [" + getDisplayPath() + "] no previous class file or flush");
LogUtil.log(config, Log.LEVEL_TRACE, "compile", "compile [" + getDisplayPath() + "] no previous class file or flush");

pcn.set(page = compile(config, classRootDir, null, false, pci != null && pci.ignoreScopes()));
flush = false;
Expand Down Expand Up @@ -423,7 +423,7 @@ public boolean releaseWhenOutdatted() {
// synchronized (SystemUtil.createToken("PageSource", getRealpathWithVirtual())) {
if (srcLastModified == 0 || srcLastModified != page.getSourceLastModified()) {// || (page instanceof PagePro && ((PagePro) page).getSourceLength() !=
// srcFile.length())
if (LogUtil.doesDebug(mapping.getLog())) mapping.getLog().debug("page-source", "release [" + getDisplayPath() + "] from page source pool");
if (LogUtil.doesTrace(mapping.getLog())) mapping.getLog().trace("page-source", "release [" + getDisplayPath() + "] from page source pool");
resetLoaded();
flush();
return true;
Expand All @@ -435,7 +435,7 @@ public boolean releaseWhenOutdatted() {
}

public void flush() {
if (LogUtil.doesDebug(mapping.getLog())) mapping.getLog().debug("page-source", "flush [" + getDisplayPath() + "]");
if (LogUtil.doesTrace(mapping.getLog())) mapping.getLog().trace("page-source", "flush [" + getDisplayPath() + "]");
pcn.page = null;
flush = true;
}
Expand Down Expand Up @@ -1075,11 +1075,13 @@ public void clear() {
*
* @param cl
*/
public void clear(ClassLoader cl) {
public boolean clear(ClassLoader cl) {
Page page = pcn.page;
if (page != null && page.getClass().getClassLoader().equals(cl)) {
pcn.page = null;
return true;
}
return false;
}

public boolean isLoad() {
Expand Down Expand Up @@ -1163,7 +1165,7 @@ public boolean executable() {
}

public void resetLoaded() {
if (LogUtil.doesDebug(mapping.getLog())) mapping.getLog().debug("page-source", "reset loaded [" + getDisplayPath() + "]");
if (LogUtil.doesTrace(mapping.getLog())) mapping.getLog().trace("page-source", "reset loaded [" + getDisplayPath() + "]");
Page p = pcn.page;
if (p != null) p.setLoadType((byte) 0);
}
Expand Down
13 changes: 10 additions & 3 deletions core/src/main/java/lucee/runtime/PageSourcePool.java
Original file line number Diff line number Diff line change
Expand Up @@ -275,23 +275,30 @@ public DumpData toDumpData(PageContext pageContext, int maxlevel, DumpProperties
*
* @param cl
*/
public void clearPages(ClassLoader cl) {
public int clearPages(ClassLoader cl) {
Iterator<SoftReference<PageSource>> it = this.pageSources.values().iterator();
PageSourceImpl psi;
SoftReference<PageSource> sr;
int count = 0;
while (it.hasNext()) {
sr = it.next();
psi = sr == null ? null : (PageSourceImpl) sr.get();
if (psi == null) continue;
if (cl != null) psi.clear(cl);
else psi.clear();
if (cl != null) {
if (psi.clear(cl)) count++;
}
else {
psi.clear();
count++;
}
}

if (cl == null) {
pageSources.clear();
}

resetWatcherWhenEmpty(false, true);
return count;
}

public void resetPages(ClassLoader cl) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public static Clazz getInstance(Class clazz, Resource dir, Log log) {
synchronized (clazz) {
cd = classes.get(clazz);
if (cd == null) {
if (log != null) log.debug("dynamic", "extract metadata from [" + clazz.getName() + "]");
if (log != null) log.trace("dynamic", "extract metadata from [" + clazz.getName() + "]");
try {
cd = new ClazzDynamic(clazz, log);
}
Expand Down
Loading
Loading