LINUX.ORG.RU

java 1000 тредов. Съедает память

 ,


1

1

Взял тестовый сорс из книги, немного его подрихтовал на запуск 1000 тредов. Запустил, меряю ps_mem -p pid и вижу, что потихонечку память процесса увеличивается. Это нормально или где-то тут протекает? :)

// Create a second thread.
class NewThread implements Runnable {
  Thread t;

  NewThread(String name) {
    // Create a new, second thread
    t = new Thread(this, name);
    System.out.println("Child thread: " + t);
  }

  // This is the entry point for the second thread.
  public void run() {
    try {
      // for (int i = 5; i > 0; i--) {
      for (int i = 5;; i--) {
        System.out.println(Thread.currentThread());
        System.out.println("Child Thread: " + i);
        Thread.sleep(500);
      }
    } catch (InterruptedException e) {
      System.out.println("Child interrupted.");
    }
    System.out.println("Exiting child thread.");
  }
}

class HelloWorld {
  public static void main(String[] args) {
    // NewThread nt = new NewThread(); // create a new thread
    // nt.t.start(); // Start the thread

    for(int x = 0; x <= 1000; x++) {
      System.out.println("aaaa");
      NewThread nt = new NewThread("some" + x); // create a new thread
      nt.t.start(); // Start the thread
    }

    try {
      for (int i = 5;; i--) {
        System.out.println("Main Thread: " + i);
        Thread.sleep(1000);
      }
    } catch (InterruptedException e) {
      System.out.println("Main thread interrupted.");
    }
    System.out.println("Main thread exiting.");
  }
}
★★★★

1 тред это один стек, который по дефолту кажись 1 метр, 500 тредов = 500 метров.

По коду, если я правильно понял, у тебя каждый тред живет 2.5 секунды (5 итераций цикла по 500 мс). Т.е. после захвата 1 гига на стеки (в течении ~5 секунд после старта приложения) вроде ничего больше создаваться не будет, потому дальше гига память расти вроде не должна. Вообще за памятью в jvm лучше следить через jvisualvm (может уже по другому называется).

Aber ★★★★★
()
Последнее исправление: Aber (всего исправлений: 1)

-Xmx то задал? Без него jvm сожрет все.

anonymous
()

Вполне нормально. Запускать так много нитей на одной виртуальной машине не так чтобы рекомендуется. А главное - зачем?

BattleCoder ★★★★★
()
Ответ на: комментарий от anonymous

Ну это сейчас они спят, что там ТС учудить хочет - отдельный вопрос.

Begemoth ★★★★★
()

Что за книжка такая странная?

Я не эксперт в джаве, но:

class NewThread implements Runnable {
  Thread t;

  NewThread(String name) {
    t = new Thread(this, name);
  }

  public void run() {}

Проще написать так:

public class NewThread extends Thread {
    @Override
    public void run() {}

}
WitcherGeralt ★★
()
Ответ на: комментарий от WitcherGeralt

Вопрос был не про количество потоков, а их связь с количеством ядер. За исключением расспаралеливания матана, количество потоков и количество ядер ни как не связаны. Прямо сейчас у меня линукс держит 500 запущенных потоков, и ничего.

RazrFalcon ★★★★★
()
Последнее исправление: RazrFalcon (всего исправлений: 1)
Ответ на: комментарий от RazrFalcon

Да хоть миллион, что ты в рамках одной программы с этими потоками собрался делать, если не cpu-bound задачи параллелить?

WitcherGeralt ★★
()
Ответ на: комментарий от Aber
for (int i = 5;; i--) {
        System.out.println(Thread.currentThread());
        System.out.println("Child Thread: " + i);
        Thread.sleep(500);
      }

Цикл без условия живет вечно

bryak ★★★★
() автор топика
Ответ на: комментарий от WitcherGeralt

Да что угодно. Особенно если мы говорим о GUI/игры, где главный поток не должен быть занят и всё вынесено в доп. потоки.

PS: я как-то видел как dropbox запускал 400 потоков на маке. Так что…

RazrFalcon ★★★★★
()
Ответ на: комментарий от bryak

Ну тогда 1 гиг на стеки почти сразу и дальше память расти не должна, сколько по факту у тебя занято?

Aber ★★★★★
()
Ответ на: комментарий от Aber

стартует со 100мб и растет. Вот это и подозрительно. По идее должна была создать стек и быть статичной т.к внутри никаких объектов не создается

bryak ★★★★
() автор топика
Последнее исправление: bryak (всего исправлений: 1)
Ответ на: комментарий от Aber

Он там стринги в циклах создаёт. Не из-за этого ли?

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

WitcherGeralt ★★
()
Ответ на: комментарий от WitcherGeralt

Он там стринги в циклах создаёт. Не из-за этого ли?

Да, так и есть, конкатенация порождает новую строку.

Aber ★★★★★
()
Ответ на: комментарий от WitcherGeralt

Собирает когда захочет, главное что 1к строк генерируется каждые пол секунды, это 1к объектов, правда они мелкие, из-за них с такими темпами едва будет сильно больше мегабайта в секунду, ну и GC должен периодически удалять мусор, так что хип должен перестать расти через несколько секунд. Это мои предложения, код не запускал. Я вот только не знаю почему приложение сразу не захватило 1 гиг на стеки для 1к тредов, видно я что-то не так себе представляю.

Aber ★★★★★
()
Ответ на: комментарий от tyamur

Тот мусор, что он печатает ¯\_(ツ)_/¯

Вероятность того, что код запускается из IDE, примерно равна единице. И тут начинают действовать побочные эффекты плюшек IDEшной консольки - например, такие - которые из-за использования непонятно чего для замера потребления памяти вполне могут влиять на результат.

izzholtik ★★★
()
Ответ на: комментарий от bryak

Так чего с Xmx? Если не задавал, то у тебя и один поток будет расти. Запусти с -Xmx16m и смотри. Думаю, будет нормально.

Legioner ★★★★★
()
Последнее исправление: Legioner (всего исправлений: 1)

ТС троллит, очевидно же. У него нет предельного значения и графика

stevejobs ★★★★☆
()

Всем спасибо. Утечка решилась путем комментирования принтов. Хотел посмотреть, сколько съедает java оперативной памяти при создании тредов

bryak ★★★★
() автор топика
Ответ на: комментарий от bryak

Утечка решилась путем комментирования принтов.

Утечка решилась путем комментирования поста на ЛОР.

//исправил

stevejobs ★★★★☆
()
Ответ на: комментарий от Aber

1 тред это один стек, который по дефолту кажись 1 метр, 500 тредов = 500 метров.

Я вот только не знаю почему приложение сразу не захватило 1 гиг на стеки для 1к тредов, видно я что-то не так себе представляю.

Стек используется по мере надобности. А не в момент создания. Задаётся лишь максимальный размер стека, и память не резервируется. И да, на Linux размер стека 8мб.

И на Windows точно также. Только размер стека по умолчанию 1мб.

fsb4000 ★★★★★
()
Ответ на: комментарий от fsb4000

А не в момент создания. Задаётся лишь максимальный размер стека

Спасибо, не знал. Помнил из времен студенческих лабораторных на сях, что в зависимости от модели памяти на x86 стек занимал либо пол сегмента, либо ему отдавался весь сегмент, так и запомнилось что он вроде как статически аллокировался :)

Aber ★★★★★
()
Ответ на: комментарий от fsb4000

Врешь по всем пунктам, кроме одного, но и там не совсем правда (речь о хотспоте, потому что у автора наверняка хотспот).

Стек используется по мере надобности. А не в момент создания.

Each Java Virtual Machine thread has a private Java Virtual Machine stack, created at the same time as the thread.

(jvms 2.5.2)

Он может расти и сжиматься, но ниже xss не будет (в хотспоте точно).

Задаётся лишь максимальный размер стека

Xss - это минимальный размер.

И да, на Linux размер стека 8мб.

Ага:

java -XX:+PrintFlagsFinal -version | grep -i stacksize
...
     intx ThreadStackSize                          = 1024                                   {pd product} {default}
     intx VMThreadStackSize                        = 1024                                   {pd product} {default}
cdshines ★★★★★
()
Ответ на: комментарий от cdshines

Он может расти и сжиматься, но ниже xss не будет (в хотспоте точно).

Но если бы он мог расти динамически то не было бы Stack Overflow Exception, а был был бы только OOM. Эх, неужели мне придется гуглить доку, я надеялся на лоре все расскажут.

Aber ★★★★★
()
Ответ на: комментарий от Aber

Я тебе уже дал ссылку на документ (jvms 2.5.2). Еще цитата:

This specification permits Java Virtual Machine stacks either to be of a fixed size or to dynamically expand and contract as required by the computation. If the Java Virtual Machine stacks are of a fixed size, the size of each Java Virtual Machine stack may be chosen independently when that stack is created.

https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.5.2

cdshines ★★★★★
()
Ответ на: комментарий от cdshines

да хоть на BSD фак, физической памяти потоки во всех вменяемых OS используют лишь сколько нужно, а сколько выделено виртуальной памяти никого не волнует.

Ну вот тебе скрин из WSL того же теста создания 10000 потоков:

https://i.imgur.com/BSZFZHM.png

Важен лишь столбец RES…

fsb4000 ★★★★★
()
Ответ на: комментарий от cdshines

64-bit Linux allows up to 128 TB of virtual address space for individual processes

и вот ссылка на табличку: https://access.redhat.com/articles/rhel-limits

Виртуальной памяти у нас можно считать неограниченно, важно лишь реальное потребление оперативки.

Даже в Windows у нас есть 8 TB для каждого процесса.

https://docs.microsoft.com/en-us/windows-hardware/drivers/gettingstarted/virtual-address-spaces

Виртуальная память со времён создания X86-64 перестала быть ресурсом.

fsb4000 ★★★★★
()
Ответ на: комментарий от fsb4000

Пробовал когда-нибудь дождаться gc в машине с засвопленной памятью? Прекрати уже говорить глупости и почитай матчасть, и перестань упрощать все до своих игрушечных примеров. Зачем умножать безграмотность? Все, что ты написал, потом кто-нибудь прочитает и наберется этой ерунды.

cdshines ★★★★★
()
Ответ на: комментарий от Aber

Вообще за памятью в jvm лучше следить через jvisualvm (может уже по другому называется).

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

bonta ★★★★★
()
Ответ на: комментарий от cdshines

Зачем умножать безграмотность?

Вот именно, не умножай безграмотность, а иди и изучай.

Виртуальная память связана со свопом примерно никак.

Виртульная память - просто какие-то диапазоны адресов резервируются для дальнейшего использования. Никакого маппинга на реальную оперативную память или своп не происходит.

Вот почитай:

https://www.mjmwired.net/kernel/Documentation/vm/overcommit-accounting

И вот тебе скриншотик: https://i.imgur.com/zIM1UiU.png

Ах, как у нас процесс отьъел 43GB виртуальной памяти, если у нас есть лишь 32GB реальной + 5GB свопа? Да ещё и показывается, что большинство памяти свободно. Картина мира @cdshines разрушена, что поделать, это жизнь…

fsb4000 ★★★★★
()
Ответ на: комментарий от fsb4000

При чем здесь это? Еще раз повторяю, jvm, которой ты выделил «неважно сколько много» виртуальной памяти, работать будет не просто плохо, а настолько отвратительно, что прямо невозможно. Это, конечно, если запускать настоящие приложения, а не игрушечные примеры, написанные с целью поддержать свое бестолковое мнение.

cdshines ★★★★★
()
Ответ на: комментарий от cdshines

Вот ещё вспомнилось что я знаю про виртуальную память:

С++ программы с включенным AddressSanitizer потребляют дохрена виртуальной памяти:

https://github.com/google/sanitizers/wiki/AddressSanitizer#ulimit--v

The ulimit -v command makes little sense with ASan-ified binaries because ASan consumes 20 terabytes of virtual memory (plus a bit).

20 терабайт!!!

Так что Virtual memory != Ram + Swap.

fsb4000 ★★★★★
()
Ответ на: комментарий от fsb4000

На реальном устройстве:

https://imgur.com/a/idZHuaK

Одноплатник с 1 гигом памяти спокойно переваривает процессы с выделенными 32 терабайтами виртуальной памяти.

fsb4000 ★★★★★
()

Ради интереса попробовал данный пример.
При запуске потребление памяти (top) примерно 150 MB, visualvm показывал <120MB.
Чем дольше работает, тем потребление ниже (через 15 минут): visualvm показывает <30 MB, а top – ~74 MB.
Пи этом всё время работы программы – потоков 1010 штук (они не завершаются, т.к. цикл бесконечный).

VM – OpenJDK 1.8.
Запуск производился в среде Eclipse.

Если запускать в консоли с использованием OpenJDK 11, то потребление top – 155 MB, visualvm – 120 MB. Потребление посоянное (видать для этого примера в восьмой версии JIT работет лучше, чем в 11-ой).

Хотелось бы у автора узнать подробности – как запускался пример?

Netzschlange
()
Ответ на: комментарий от fsb4000

Так что Virtual memory != Ram + Swap

Года два назад при использовании Eclipse в связке с GTK3 потребление виртуальной памяти доходило до 9 TB (террабайт).
При этом опреативы было 32 GB, а swap – 64 GB. А вот резидентной памяти потреблялось в соответствии с Xmx – 2 GB.
При этом то же самый Eclipse, на той же машине, но с использванием GTK2 уже не был таким прожорливым в плане виртуальной памяти – хватало нескольких GB, при тех же 2GB резидентной.

Netzschlange
()
Ответ на: комментарий от WitcherGeralt

Да и не должен вроде, они должны кэшироваться в стринг пуле. Скорее всего поэтому и память отъедалась.

JAkutenshi ★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.