coldfusion.util.Key Утечка памяти - проблема со структурными ключами? - PullRequest
5 голосов
/ 05 мая 2011

Соответственно, я могу сгенерировать исключение Java Heap Space OutOfMemory со следующим кодом в ColdFusion 9.01 (еще не пробовал более ранние версии):

<cfset uuidGenerator = createObject("java", "java.util.UUID")>
<cfset transient = structNew()>
<cfloop from="1" to="100000" index="index">
    <cfset transient[uuidGenerator.randomUUID().toString()] = true>
</cfloop>

Приведенный выше код использует класс Java UUID, потому что он быстрее, чем ColdFusion. Сама структура не существует после запроса (то есть она не находится в какой-то постоянной области видимости, такой как application).

В качестве теста я генерирую дамп кучи сразу после инициализации сервера. Затем я запускаю этот код несколько раз и вижу заполнение штатного поколения через jConsole. После я запускаю еще одну кучу дампов. С помощью отчета об утечке из Eclipse Memory Analysis Tool я вижу один большой объект с корнем coldfusion.util.Key.

Я спрашиваю здесь в надежде, что другие столкнулись с подобной проблемой, и если так, что они сделали, чтобы обойти это.

Ответы [ 3 ]

3 голосов
/ 24 февраля 2012

Не идеальное решение, но пока Adobe не устранит утечку памяти изнутри, вы можете получить доступ к закрытому члену ConcurrentHasMap для объекта coldfusion.util.Key и очистить его вручную.

Мы настроили запланированное задание на выполнение этой ночи, а затем сразу же выполнили GC.

Скомпилируйте это в файл JAR и поместите его где-нибудь в путь к классу ColdFusion.

import coldfusion.util.Key;
import java.lang.reflect.Field;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

class KeyEx
{
    public KeyEx()
    {
    }

    public void resetCache(Object k)
    {
        try
        {
            Field f = Key.class.getDeclaredField("keys");
            f.setAccessible(true);
            ConcurrentHashMap chm = (ConcurrentHashMap)f.get(k);
            chm.clear();
        }
        catch (Exception ex)
        {
            System.out.println("ZOMG something went epically wrong!");
        }
    }
}

А затем вы можете просто создать новый объект KeyEx в coldfusion и вызвать resetCache, передав в синглтон coldfusion.util.Key.

<cfset keys = createObject("java", "coldfusion.util.Key") />
<cfset x = createObject("java", "KeyEx").init() />
<cfset x.resetCache(keys) />
2 голосов
/ 22 февраля 2012

Эти два примера тестов, вероятно, иллюстрируют проблему немного яснее:

- MemoryLeak.cfm

<cfset transient = structNew() />
<cfset base = getTickCount() />
<cfloop from="1" to="10000" index="index">
  <cfset transient[hash("#base##index#")] = true >
</cfloop>
<cfoutput>
Done
</cfoutput>

- NoMemoryLeak.cfm

 <cfset transient = structNew() />
 <cfloop from="1" to="10000" index="index">
 <cfset transient[hash("#index#")] = true >
 </cfloop>
 <cfoutput>
 Done
 </cfoutput>

Ударьте MemoryLeak.cfm по коробке CF 9.01+ 100 раз, и утечка памяти действительно очевидна. Перезапустите JVM и нажмите NoMemoryLeak.cfm столько раз, сколько захотите, и OldGen даже не заметит этого. Я получил до 500 000 раз, прежде чем сдаться.

Я не вижу исходную ошибку OrangePips # в базе ошибок CF (похоже, все старые ошибки были добавлены в обновлении?), Поэтому я создал новый статус https://bugbase.adobe.com/index.cfm?event=bug&id=3119991, подтвержденный в настоящее время, и ToFix.

0 голосов
/ 06 мая 2011

ОК, вот что я и сделал, чтобы отследить основную причину.Дампы кучи показывали мне, что coldfusion.util.Key.keys держал множество объектов в ConcurrentHashMap типа coldfusion.util.Key после того, как я некоторое время запускал нагрузочный тест.Поэтому я выяснил, какой .jar содержит .class - /lib/cfusion.jar - и декомпилировал его, чтобы увидеть источник.Я видел, что он хранит эти ссылки в статическом закрытом поле, ключи которого никогда не удаляются.

По сути, этот объект создается каждый раз, когда ключ, никогда ранее не использовавшийся, вставляется в структуру.Так что он вызывается со всех приложений ColdFusion.Я считаю, что цель состоит в том, чтобы ускорить поиск, невосприимчивость к регистру и сравнение.

Признавая, что я не могу изменить код, чтобы исправить проблему, поскольку я уверен, что при использовании в производственной среде это нарушит лицензионное соглашение, я изменил код, чтобы помочь мне понять, почему так много объектовсоздано в первую очередь.В основном я добавил следующее в конструктор:

try {
  throw new RuntimeException();
} catch (Exception e) {
  e.printStackTrace(System.out);
}

Затем я скомпилировал и поместил измененный класс в cfusion.jar.

К счастью, генерируемая трассировка стека включает имена файлов .cfm и номера строк.Я запустил CF из командной строки (т.е. jrun.exe -start coldfusion), чтобы я мог легко увидеть, что происходит с System.out, а затем снова запустил свой нагрузочный тест.Таким образом, эвристик находил, какой код называл этот много и, возможно, изменял это.

Это позволило мне быстро увидеть файл .cfm, который вызывался при каждом запросе.Таким образом, я могу изменить этот файл, чтобы он больше не создавал структурные ключи, и это привело к гораздо более длительной работе виртуальной машины.Это не решает проблему полностью, но значительно снижает потребление памяти.

...