RE:NODE

რესურსები13 წუთის საკითხავი

Node.js-ის მეხსიერების ლიმიტები: heap, RSS და 2 GB-ის ჭერი

რატომ კვდება Node აპლიკაცია 2 GB-ზე 4 GB-იან გეგმაზე: V8 heap-ის ლიმიტი კონტეინერის ლიმიტის წინააღმდეგ, --max-old-space-size-ის დაყენება და ნამდვილი გაჟონვის პოვნა.

განახლებულია

0 მკითხველი

Node პროცესი 4 GB-იან გეგმაზე, რომელიც კვდება მაშინ, როცა პანელის გრაფიკზე 2 GB თავისუფალია, ჰოსტინგის შეცდომა არ არის. Node-ს საკუთარი heap-ის ჭერი აქვს, რომელსაც გაშვებისას ევრისტიკა ადგენს და შენს გეგმას არ კითხულობს, და ის ხშირად იმაზე გაცილებით დაბალია, რაშიც იხდი. სარკისებური შემთხვევა ისეთივე გავრცელებულია და საერთოდ არ ჰგავს პირველს: heap, რომელიც კონტეინერის მთელ ზომაზეა დაყენებული, რაც გარანტიას იძლევა, რომ ბირთვი პროცესს მანამდე მოკლავს, სანამ V8 ნაგვის შეგროვებას მოასწრებს. ორივე იძლევა "out of memory"-ს, გამოსავლები საპირისპიროა და ორი ავარია ადვილად გასარჩევია, როცა იცი, რას უყურო. ეს არის, როგორ გაარკვიო, რომელი გაქვს, სწორად დააყენო რიცხვი და გაიგო, საერთოდ გჭირდებოდა თუ არა რამის ზრდა.

ორი ჭერი და რომელს ხვდები პირველს#

კონტეინერის ლიმიტი ის არის, რასაც ჰოსტი სერვერს აძლევს. ეს cgroup პარამეტრია, ბირთვის მიერ აღსრულებადი, და ის მთელ პროცესს ითვლის: JavaScript heap-ს, ყველა Buffer-ს, ყველაფერს, რასაც native მოდული გამოყოფს, thread stack-ებს, JIT-ის კომპილირებულ კოდს და თავად Node-ის binary-ს. გადალახავ და ბირთვი გიგზავნის SIGKILL-ს. გაფრთხილება არ არის, stack trace არ არის და შენს კოდს არაფრის ლოგირების შესაძლებლობა არ რჩება, რადგან პროცესს გაჩერებას არ სთხოვენ - მას აჩერებენ.

V8 heap-ის ლიმიტი ის არის, რამდენსაც Node საკუთარ JavaScript ობიექტებს ანიჭებს. მას V8 აღასრულებს და არა ბირთვი. როცა V8 ვერ ათავისუფლებს საკმარისს გამოყოფის დასაკმაყოფილებლად, ის განზრახ თმობს, ბეჭდავს რამდენიმე აბზაცს და წყდება.

ხაზზე ვიღუპებითV8 old space--max-old-space-sizeBuffer-ები, nativeheap-ის გარეთThread-ები, კოდიlibuv, JIT, binaryპროცესის RSSრასაც ბირთვი ითვლისკონტეინერის ლიმიტიშენი გეგმა
ორი ჭერი ერთ Node პროცესზე

სასარგებლო შედეგი: JavaScript heap პროცესის ქვესიმრავლეა, და --max-old-space-size მხოლოდ ამ ქვესიმრავლეს მართავს. აპლიკაცია, რომელიც ფაილების ატვირთვას ნაკადად ატარებს, სურათებს ზომას უცვლის ან დიდ Buffer ობიექტებს ინახავს, შეიძლება 300 MB heap-ზე და 2.5 GB RSS-ზე იჯდეს. გეგმის ზომის განსაზღვრა მხოლოდ heapUsed-ით ასეთ აპლიკაციას კლავს გეგმაზე, რომელიც საჭიროზე ორჯერ დიდი ჩანს.

რა არის რეალურად Node პროცესის შიგნით#

process.memoryUsage() ხუთ რიცხვს აბრუნებს და თითოეული სხვა კითხვას პასუხობს:

javascript
{  rss: 215547904,        // 205 MB - the whole process in physical memory  heapTotal: 132513792,  // 126 MB - heap V8 has committed from the OS  heapUsed: 118426160,   // 113 MB - live objects plus uncollected garbage  external: 8451234,     //   8 MB - C++ objects tied to JS objects  arrayBuffers: 4210688  //   4 MB - ArrayBuffers and Buffers, part of external}
ველიითვლება heap-ის ლიმიტშიითვლება კონტეინერის ლიმიტში
heapUsedდიახდიახ
heapTotalდიახდიახ
externalარადიახ
arrayBuffersარადიახ
rssარ ეხებაეს არის რიცხვი, რომელსაც ბირთვი უყურებს

პროდუქციაში ლოგირება გაუკეთე ხუთივეს წუთში ერთხელ. ეს არაფერი ჯდება და "ისევ მოკვდა"-ს ისეთ ფორმად აქცევს, რომლის წაკითხვაც შეიძლება. ყველაზე ღირებული ნიმუში, რომელსაც ის ავლენს, არის rss-ის ზრდა heapUsed-ის უცვლელობისას, რაც ნიშნავს, რომ გაჟონვა buffer-ებშია ან native დანამატში და heap-ის არანაირი დარეგულირება მას ვერ შეეხება.

რაც RSS-ში ცხოვრობს და heapUsed-ში არასდროს ჩანს: Buffer-ის შიგთავსი, ნებისმიერი რამ, რასაც native მოდული გამოყოფს, როგორიცაა სურათის ან crypto ბიბლიოთეკა, ოთხი libuv thread-pool thread-ი (UV_THREADPOOL_SIZE, ნაგულისხმევად 4) და მათი stack-ები, JIT-ის კომპილირებული გამოსავალი, ჩატვირთული binary და allocator-ის ფრაგმენტაცია. ბოლო რეალურია და ხშირად არასწორად სვამენ დიაგნოზს - glibc-ის malloc ინახავს thread-ზე arena-ებს და გათავისუფლებულ გვერდებს ყოველთვის არ უბრუნებს ბირთვს, ამიტომ RSS შეიძლება იმაზე მაღლა გაჩერდეს, რასაც აპლიკაცია იყენებს. გარემოში MALLOC_ARENA_MAX=2-ის დაყენება ზოგჯერ ამცირებს; ღირს ერთი ექსპერიმენტი და არა მთელი შუადღე.

შენი რეალური heap-ის ლიმიტის პოვნა ერთი ბრძანებით#

ნაგულისხმევს ნუ გამოიცნობ. ის Node-ის მთავარ ვერსიებს შორის შეიცვალა, და Node 12-დან ის გამოითვლება იმის მიხედვით, რამდენ მეხსიერებას თვლის Node მანქანაზე - რაც კონტეინერის შიგნით ჩვეულებრივ მთელი ჰოსტია და არა შენი გეგმა. 128 GB-იან node-ზე გაშვებულ 1 GB კონტეინერში Node სიამოვნებით აირჩევს heap-ის ჭერს შენს ლიმიტზე რამდენჯერმე მაღალს, და ბირთვი გკლავს ბევრად ადრე, ვიდრე V8 რამეს გააკეთებს.

სამაგიეროდ, ჰკითხე runtime-ს:

bash
$ node -p "require('v8').getHeapStatistics().heap_size_limit / 1048576"2048.0000152587891$ node --max-old-space-size=3072 -p "require('v8').getHeapStatistics().heap_size_limit / 1048576"3075.0000152587891

მეორე რიცხვი ოდნავ აღემატება flag-ს, რადგან heap_size_limit old space-ის გარდა young generation-საც მოიცავს. კიდევ ორი ერთსტრიქონიანი ღირს ცოდნად:

bash
$ node -p "require('os').totalmem() / 1073741824"   # the machine, not your plan$ node -p "process.constrainedMemory()"             # the cgroup limit, in bytes

process.constrainedMemory() არსებობს Node 18.15-სა და უფრო ახალზე და აბრუნებს მეხსიერების ლიმიტს, რომელსაც კონტეინერი აცხადებს, ან 0-ს, როცა ლიმიტი არ არის. ძველ ვერსიებზე ის განსაზღვრული არ არის და ერთადერთი ხელმისაწვდომი არის os.totalmem() - რაც ზუსტად ზემოთ აღწერილი ხაფანგია. თუ შენი process manager ან Dockerfile გადაწყვეტილებებს os.totalmem()-ით იღებს, შეამოწმე ისინი.

ლიმიტის სწორად დაყენება#

ამას სამი ადგილი აკეთებს და ისინი ექვივალენტური არ არის:

bash
$ node --max-old-space-size=3072 dist/server.js
the panel Startup tab, or .env
NODE_OPTIONS=--max-old-space-size=3072
package.json
{  "scripts": {    "start": "node --max-old-space-size=3072 dist/server.js"  }}

NODE_OPTIONS ვრცელდება ყველა Node პროცესზე, რომელსაც გარემო უშვებს, მათ შორის npm, next build, tsc და ნებისმიერი შვილობილი პროცესი. ხშირად ეს არის ზუსტად ის, რაც გინდა - Next.js-ის build, რომელიც შუაში კვდება, კლასიკური შემთხვევაა - მაგრამ გახსოვდეს, რომ build ნაბიჯი და გაშვებული სერვერი ამ შემთხვევაში ერთ რიცხვს იყენებს.

heap დააყენე კონტეინერის ლიმიტის დაახლოებით 75%-ზე. არასდროს გაუტოლო გეგმას.

კონტეინერის ლიმიტი--max-old-space-sizeრისთვის რჩება დანარჩენი
512 MB320Runtime, stack-ები, პატარა buffer-ები
1 GB768იგივე, პლუს მოკრძალებული cache-ები
2 GB1536ადგილი რამდენიმე დიდი მოთხოვნისთვის
4 GB3072ნორმალური მარაგი
8 GB6144ნორმალური მარაგი

ამაზე მეტი დატოვე, თუ ფაილებს იღებ, სურათებს აგენერირებ ან იყენებ native მოდულს, რომელიც heap-ის გარეთ გამოყოფს. სერვისს, რომელიც მოთხოვნაზე 200 MB ფაილს ბუფერავს, მარაგში საშუალო კი არა, პარალელურობა გასამრავლებელი აქვს.

ორი მეზობელი flag ზოგჯერ ღირს შეხებად. --max-semi-space-size=32 წევს young generation-ს, რაც ეხმარება აპლიკაციებს მოკლევადიანი ობიექტების ძალიან მაღალი გამოყოფის სიჩქარით - ნაკლები ობიექტი გადადის old space-ში და გადარჩება შეგროვებას, რასაც არ უნდა გადარჩენოდა. ორი semi-space გამოიყოფა, ამიტომ რეალური ფასი დაყენებულ რიცხვზე სულ მცირე ორჯერ მეტია. მიმართე მას მხოლოდ მაშინ, როცა --trace-gc მუდმივ scavenge-ებს აჩვენებს promotion-ის მზარდი მაჩვენებლით.

და თუ worker thread-ებს ან cluster-ს უშვებ, თითოეულს საკუთარი heap აქვს:

javascript
new Worker("./job.js", {  resourceLimits: { maxOldGenerationSizeMb: 512, maxYoungGenerationSizeMb: 32 },});

ოთხი cluster worker, რომლებიც NODE_OPTIONS-იდან --max-old-space-size=3072-ს იღებენ 4 GB გეგმაზე, გარანტირებული kill-ია, და ეს ძალიან ადვილი შეცდომაა, როცა ერთი პროცესიდან რამდენიმეზე გადადიხარ. გაყავი ბიუჯეტი. PM2 თუ ჰოსტინგის პანელი განიხილავს, როდის გინდა საერთოდ რამდენიმე პროცესი, რაც ერთბირთვიან გეგმაზე ჩვეულებრივ არასდროს.

ორი ავარიის ნიშანი და მათი გარჩევა#

V8 heap-ის ლიმიტი, სრულად:

code
<--- Last few GCs --->[18:0x5a3b0f0]   109238 ms: Mark-Compact 2010.4 (2078.3) -> 2009.1 (2080.0) MB[18:0x5a3b0f0]   110114 ms: Mark-Compact 2011.0 (2080.0) -> 2010.2 (2081.5) MB<--- JS stacktrace --->FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed -JavaScript heap out of memory

კონტეინერის ლიმიტი, სრულად:

code
2026-05-04T01:58:12.004Z  GET /api/reports/daily 200 1182ms

ეს არის მთელი ნიშანი: გამოტანა შუაში წყდება და პროცესი აღარ არის. exit კოდები საკითხს წყვეტს, როცა კონსოლი უკვე გადაგორდა:

Exit კოდისიგნალიმნიშვნელობა
137SIGKILL (128 + 9)მოკლულია გარედან. კონტეინერზე თითქმის ყოველთვის მეხსიერების ლიმიტი
134SIGABRT (128 + 6)V8-მა თავი შეწყვიტა. heap-ის ლიმიტი ან assertion
139SIGSEGV (128 + 11)Segmentation fault, ჩვეულებრივ native მოდული
143SIGTERM (128 + 15)მოთხოვნილია სუფთა გაჩერება. შენი restart ღილაკი ან deploy
1-შენმა კოდმა ისროლა და არავინ დაიჭირა

კიდევ ორი შეცდომა იგივე ტანსაცმლით დადის, ორიდან არც ერთი ჭერი რომ არ არის:

  • RangeError: Invalid string length არის V8-ის სტრიქონის მაქსიმალური ზომა და არა მეხსიერების ამოწურვა. რეალური მნიშვნელობა შეამოწმე node -p "require('buffer').constants.MAX_STRING_LENGTH"-ით. ის ნიშნავს, რომ შეაერთე ან JSON.stringify გაუკეთე რამეს, რაც ნაკადად უნდა გატარებულიყო.
  • ERR_WORKER_OUT_OF_MEMORY არის ერთი worker thread-ი, რომელიც საკუთარ resourceLimits-ს აჭარბებს, და მთავარი პროცესის heap-ს საერთოდ არ ეხება.

RE:NODE-ზე კონტეინერის ლიმიტის შემთხვევას კონკრეტული და განზრახ ფორმა აქვს. მეხსიერების ლიმიტზე ბირთვი კონტეინერს აჩერებს და ის სუფთად ბრუნდება, swap-ში ჩავარდნის ნებართვის ნაცვლად, რასაც upstream Pterodactyl პირიქით აკეთებს. მოკლე restart, რომელიც თავისით გამოჯანმრთელდება, ჯობია ნელ დაცემას, რომელიც მთელ მანქანას თან იყოლიებს. შედეგი, რისთვისაც უნდა მოემზადო: გაჟონილი აპლიკაცია restart loop ხდება, და watcher ყოველ ორ წუთში ამოწმებს uptime-ს, რომელიც უკან წავიდა: საათში სამი მოულოდნელი restart სერვერის გვერდზე გაფრთხილებას აჩენს და ავტომატურად ხსნის ticket-ს. რატომ რესტარტდება შენი გეიმ სერვერი განუწყვეტლივ იმავე მექანიზმს მეორე ბოლოდან განიხილავს.

გაჟონვის პოვნა ჭერის აწევის ნაცვლად#

აპლიკაცია, რომელიც ნებისმიერ ჭერამდე იზრდება, გაჟონვას განიცდის, და ჭერის აწევა საათებს გიგებს და არა გამოსწორებას. თუ მეხსიერება მუდმივი დატვირთვისას სტაბილურად იზრდება, შესაცვლელი რიცხვი შენს კოდშია და არა გაშვების ბრძანებაში.

დაიწყე --trace-gc-ით, რომელიც თითქმის უფასოა და პროდუქციაშიც შეიძლება:

bash
$ node --trace-gc --max-old-space-size=1536 dist/server.js
code
[42:0x5f8c0a0]     3021 ms: Scavenge 58.3 (79.5) -> 44.1 (80.5) MB, 2.1 / 0.0 ms[42:0x5f8c0a0]   184433 ms: Mark-Compact 512.9 (548.0) -> 498.2 (549.5) MB, 220.4 / 0.0 ms[42:0x5f8c0a0]   402117 ms: Mark-Compact 899.1 (932.0) -> 884.7 (933.5) MB, 388.9 / 0.0 ms

Scavenge ხაზებს ნუ დაუჯერებ. წაიკითხე რიცხვი, რომელიც ისრის უშუალოდ შემდეგ მოდის მიმდევარ Mark-Compact ხაზებზე: ეს არის ცოცხალი heap სრული შეგროვების შემდეგ. თუ ის ყოველ ჯერზე დაახლოებით იმავე ფსკერზე ბრუნდება, გაჟონვა არ გაქვს და შეიძლება მართლა დიდი heap გჭირდებოდეს. თუ ის საათების განმავლობაში მონოტონურად იზრდება - 498, 884, 1,340 - რაღაც შენარჩუნებულია, და არანაირი პარამეტრი ამას არ გამოასწორებს.

როცა დაადგინე, რომ გაჟონვაა, გადაიღე heap snapshot-ები:

bash
$ node --heapsnapshot-signal=SIGUSR2 dist/server.js$ kill -USR2 $(pgrep -f dist/server.js)

ყოველი სიგნალი წერს .heapsnapshot ფაილს სამუშაო დირექტორიაში. ამას პროცესის შიგნიდანაც შეგიძლია, რაც პანელზე ადვილია, სადაც სიგნალების გაგზავნა უხერხულია:

javascript
const v8 = require("node:v8");const path = v8.writeHeapSnapshot(`/home/container/heap-${Date.now()}.heapsnapshot`);console.log("snapshot written to", path);

გადაიღე ერთი გახურების შემდეგ და მეორე ოცდაათი-სამოცი წუთის მერე იმავე დატვირთვაზე. ჩამოტვირთე ორივე, გახსენი Chrome DevTools, გადადი Memory პანელზე, ჩატვირთე ორივე ფაილი, შემდეგ გადართე ხედი Comparison-ზე და დაალაგე delta-ით. კონსტრუქტორი, რომლის ობიექტების რაოდენობა ათიათასობით გაიზარდა, გაჟონვაა, და retainers პანელი გეუბნება, რა ჭერს მას. ორი snapshot მთელი ტექნიკაა; ერთი snapshot თითქმის არაფერს გეუბნება.

სად მიდის მეხსიერება ჩვეულებრივ#

დაახლოებით იმ თანმიმდევრობით, რომლითაც ეს რეალურ აპლიკაციებში ჩნდება:

  • შეუზღუდავი cache. მოდულის დონის Map ან უბრალო ობიექტი, რომელშიც მხოლოდ იწერება. მიეცი ზომის ლიმიტი და ამოგდების პოლიტიკა; max-იანი LRU ოცი ხაზია ან ერთი dependency.
  • Event listener-ები, რომლებიც თითო მოთხოვნაზე ან კავშირზე ემატება და არასდროს იშლება. MaxListenersExceededWarning შენს ლოგში არის Node, რომელიც გეუბნება, რომ ეს ხდება, და ის თითქმის არასდროს არის ცრუ განგაში.
  • Timer-ები, რომლებიც closure-ებს ინახავენ. თითო კავშირზე შექმნილი და არასდროს გასუფთავებული setInterval სამუდამოდ ცოცხალს ინახავს ყველაფერს, რასაც მისი callback ეხება, მათ შორის მოთხოვნის სხეულებს და ბაზის რიგებს.
  • მოლოდინში მყოფი მოთხოვნების map-ები timeout-ის გარეშე. ინდექსირებულია correlation id-ით, პასუხზე წაიშლება, პასუხის არმოსვლისას არასდროს.
  • Stream-ები, რომლებიც არ მოიხმარება ან არ ნადგურდება. backpressure-ის იგნორირება - იმაზე სწრაფად წერა, ვიდრე დანიშნულება იღებს - სხვაობას დიზაინით მეხსიერებაში ბუფერავს.
  • Discord bot-ის cache-ები. discord.js მეხსიერებაში ინახავს guild-ებს, არხებს, წევრებს, შეტყობინებებს და presence-ებს. ნაგულისხმევები ერგება bot-ს რამდენიმე სერვერში; წევრების cache-ები განსაკუთრებით იზრდება ყოველ guild-თან, რომელსაც უერთდები. გაუწერე makeCache და sweepers და მოითხოვე მხოლოდ ის gateway intent-ები, რომლებსაც რეალურად იყენებ. Discord bot-ის ჰოსტინგი 24/7 და გეგმის არჩევა Discord bot-ისთვის ზომებს განიხილავს.
  • Native მოდულები. სურათების დამუშავება, headless ბრაუზერები და encoding ბიბლიოთეკები heap-ის გარეთ გამოყოფენ. ისინი ჩანს როგორც rss-ისა და external-ის ზრდა heapUsed-ის უცვლელობისას, და მათ დიდი heap კი არა, საკუთარი პარალელურობის ლიმიტი სჭირდებათ.

გეგმის ზომის განსაზღვრა, მაგალითით#

უხეში საწყისი წერტილები ერთი პროცესისთვის, სანამ საკუთარს გაზომავ:

დატვირთვატიპური RSSგონივრული გეგმა
Discord bot, რამდენიმე guild80-150 MB1 GB
Discord bot, ათეულობით guild250-600 MB2 GB
Express ან Fastify API, ზომიერი ტრაფიკი150-400 MB1-2 GB
Next.js პროდუქციაში (next start)300-700 MB2 GB
next build ან დიდი tsc გაშვება1.5-4 GB პიკზეგააკეთე build იქ, სადაც ადგილი გაქვს
სურათების ან მედიის დამუშავება500 MB და მეტი, თითო ამოცანაზემთლიანად შეყვანის ზომაზეა დამოკიდებული

ახლა რეალური ფორმა. Express API 2 GB გეგმაზე თითქმის ყოველ ღამე დაახლოებით 02:00-ზე კვდებოდა. კონსოლი შუა მოთხოვნაზე წყდებოდა, exit კოდი იყო 137 და აპლიკაციის ლოგში არაფერი იყო. NODE_OPTIONS დაყენებული იყო --max-old-space-size=2048-ზე, გეგმის ზუსტი ტოლი, და ეს არის შეცდომა: heap-ს მთელი კონტეინერის შევსება შეეძლო, ამიტომ V8-ს აგრესიულად შეგროვების მიზეზი არ ჰქონდა და ბირთვი პირველი მივიდა.

წუთობრივმა process.memoryUsage() ლოგმა აჩვენა heapUsed მთელი დღე 180 MB-ზე უცვლელი და შემდეგ ვერტიკალური ზრდა 1.7 GB-მდე 01:55-დან. 01:55-ზე დაგეგმილმა ამოცანამ ოთხასი ათასი ჩანაწერი ამოიღო, ობიექტებად გადააქცია და შედეგს JSON.stringify გაუკეთა ერთ სტრიქონად, რომ ანგარიში დაეწერა.

სამი ცვლილება, არც ერთი დიდი გეგმა არ არის. heap ჩამოვიდა 1536-ზე, რომ V8 საკუთარ ჭერს პირველი მიაღწიოს და წასაკითხი შეტყობინებით შეწყდეს გაქრობის ნაცვლად. ანგარიშის ამოცანა გადავიდა cursor-სა და write stream-ზე, ამიტომ რიგები არასდროს არის ყველა ერთად მეხსიერებაში. და ამოცანა საერთოდ გავიდა API პროცესიდან - შაბლონებისთვის იხილე ფონური ამოცანები პატარა სერვერზე. შემდეგ პიკური RSS იყო 620 MB იმავე 2 GB გეგმაზე.

მაგალითის ილუსტრირებული ზოგადი წესი: მეხსიერების ყიდვამდე გააკეთე ისე, რომ პროცესი ისეთნაირად ვარდებოდეს, რომ რაღაცას ბეჭდავდეს. 134 stack trace-ით უფრო ღირებულია, ვიდრე 137 სიჩუმით, თუმცა 137 მეგობრულად გამოიყურება.

FAQ#

რას აყენებს რეალურად --max-old-space-size?

V8-ის old generation-ის მაქსიმალურ ზომას მეგაბაიტებში - JavaScript heap-ის იმ ნაწილს, სადაც ობიექტები ცხოვრობენ, მას შემდეგ რაც young-generation-ის ორ შეგროვებას გადაურჩებიან. ის არ ზღუდავს Buffer მონაცემებს, native გამოყოფებს და thread stack-ებს, ამიტომ პროცესს შეუძლია და გამოიყენებს კიდეც იმაზე მეტ მეხსიერებას, ვიდრე დაყენებული რიცხვია.

რომელია უსაფრთხო მნიშვნელობა 4 GB გეგმაზე?

დაახლოებით 3072. მიზანია, V8-მა საკუთარ ჭერს ბირთვის ჭერზე ადრე მიაღწიოს, რომ ჩუმი kill-ის ნაცვლად წასაკითხი შეცდომა მიიღო. თუ შენი აპლიკაცია დიდ buffer-ებს ამოძრავებს, დაბლა დადი და უყურე rss-ს და არა heapUsed-ს.

ჩემი აპლიკაცია მოკლულია, მაგრამ ლოგში არაფერია. რატომ?

იმიტომ, რომ ბირთვმა ის SIGKILL-ით მოკლა კონტეინერის მეხსიერების ლიმიტის გადაჭარბებისთვის, და პროცესი, რომელიც SIGKILL-ს იღებს, ვერანაირ კოდს ვერ გაუშვებს, logger-ს ჩათვლით. შეამოწმე exit კოდი: 137 სწორედ ეს შემთხვევაა. კონსოლის კითხვა განიხილავს, როგორ გამოიყურება უეცარი დასასრული ნამდვილი ავარიის გვერდით.

აღმოფხვრის heap-ის ლიმიტის აწევა მეხსიერების გაჟონვას?

არა. ის ავარიას აგვიანებს იმის პროპორციულად, რამდენიც აწიე. თუ ცოცხალი heap ყოველი სრული garbage collection-ის შემდეგ მუდმივი დატვირთვისას სტაბილურად იზრდება, შენარჩუნებული ობიექტები შეცდომაა, და ერთადერთი რეალური გამოსავალია იმის პოვნა, რა ჭერს მათ.

იზიარებენ cluster worker-ები heap-ის ლიმიტს?

არა. ყველა პროცესს საკუთარი აქვს, და NODE_OPTIONS ერთსა და იმავე მნიშვნელობას ყველას ანიჭებს. ოთხი worker, თითო 3 GB-ით 4 GB გეგმაზე, გეგმაზე ოთხჯერ მეტია. flag-ის დაყენებამდე გაყავი შენი კონტეინერის ლიმიტი პროცესების რაოდენობაზე.

რატომ რჩება RSS მაღალი ტრაფიკის ტალღის შემდეგ?

ნაწილობრივ იმიტომ, რომ V8 გათავისუფლებულ heap გვერდებს ოპერაციულ სისტემას მაშინვე არ უბრუნებს, და ნაწილობრივ იმიტომ, რომ სისტემური allocator arena-ებს იტოვებს. პლატო ნორმალურია. კიბე, რომელიც დღეების განმავლობაში მხოლოდ მაღლა მიდის, ნორმალური არ არის - ეს არის გრაფიკის ფორმა, რომელზეც უნდა იმოქმედო, და სერვერის დატვირთვის გრაფიკის კითხვა დანარჩენ ფორმებს განიხილავს.


კომენტარები

სრულიად ანონიმურად: ანგარიშის, ელფოსტის და cookie-ის გარეშე. ინახება მხოლოდ სახელი, ტექსტი და დრო - სხვა არაფერი. ბმულების რაოდენობა ლიმიტირებულია.

0/2000