Содержание

пятница, 8 апреля 2011 г.

Java thread stack

Данная область памяти связана с процессом Java и не относится ни к Heap, ни к Prmanent области и, вообще, не отображается JConsole и не входит в Heap dump, т.е. её невозможно увидеть различными анализаторами памяти. Давайте разберемся, что же там лежит и как ею управлять.

Данная память выделяется каждому созданному потоку в индивидуальное пользование. Там хранится стек вызова методов, локальные переменные и параметры. Тут я дам небольшое пояснение, так как было время, когда я сам в этом немного путался. Если вы создаете и присваиваете локальную переменную примитивного типа, то все данные полностью хранятся на стеке. Если же вы создаете объект, то ссылка хранится на стеке, сам же объект уже создается в куче. При создании массива примитивных типов происходит то же самое что и с объектами: ссылка на массив храниться на стеке, а сам массив в куче. Однако начиная с Java 6 Update 14 можно включить так называемый Escape-Analysis, который будет размещать объекты не покидающие метода тоже на стеке. Сделать это можно выставив параметр -XX:+DoEscapeAnalysis. Но он будет работать только в -server моде. Подробнее об этом можете почитать тут. Кстати в Java 6 Update 18 данную опцию отключили, а в Java 6 Update 21 (6873799) вернули и сделали по умолчанию. Таким образом в вашей JVM запущенной на сервере данная оптимизация уже работает, если вы используете java6u21 и выше. Размещение объектов и примитивов на стеке несказанно упрощает жизнь сборщику мусора. Пользуйтесь этим - создавайте небольшие маложивущие immutable объекты вместо часто изменяемых долгоживущих.

Размер стека выделяемого потоку по умолчанию можно поменять параметром JVM -Xss. Либо при создании конкретного потока передать параметр в конструкторе Thread(ThreadGroup group, Runnable target, String name,long stackSize). Но перед тем как его использовать, обязательно проверьте поддерживается ли он на вашей платформе, так как данный параметр в конструкторе по спецификации является рекомендательным и может быть не реализован.

Будьте аккуратны с изменением размера стека. При выставлении слишком большого значения и наличии большого количества потоков вы можете получить OutOfMemoryError или какой-нибудь другой internal error. При выставлении же слишком маленького значения вы можете получить StackOverflowError. На самом деле StackOverflowError можно получить и на стеках достаточно больших размеров, если вы используете глубокую рекурсию.

Анализ длины стека можно провести несколькими способами. В любой момент вы можете снять thread dump послав сигнал kill процессу. В UNIX системе это команда "kill -3". Под windows вы можете использовать программку SendSignal. Можете быть спокойны, ваш процесс от этого не умрет и будет работать дальше как и раньше. Так же получить thread dump можно через JMX, как через API так и через JConsole. Правда тут есть небольшое неудобство, так как результаты попадут в stdout и там будут перечислены стеки всех ваших потоков, которых может быть тысячи. Кстати, чтобы среди этих тысяч потоков найти именно тот, который интересует вас, есть хоршая практика задавать имя потока при его создании. Особенно это касается Timer и Executors. В последнем это можно сделать передав специальную ThreadFactory при создании.

Если же вас интересует только стек конкретного потока в конкретном месте, то вы можете это сделать программно через JMX используя ThreadInfo либо использовать следующий метод: