თამაშის სერვერს, რომელსაც მეხსიერების გაჟონვა აქვს, მეხსიერების "იატაკი" დღითიდღე ეზრდება და არასდროს ეცემა, სანამ პროცესი არ გადაიტვირთება. ეს სქემა ერთადერთი საიმედო ნიშანია. მეხსიერება, რომელიც რამდენიმე საათი იზრდება და შემდეგ სწორდება, cache-ია ან heap, რომელიც თავის დაკონფიგურირებულ ზომამდე ივსება, ხოლო მეხსიერება, რომელიც გამოკვლეულ სამყაროსთან ერთად იზრდება, ზრდაა და არა გაჟონვა. როცა დარწმუნდები, რომ ნამდვილი გაჟონვა გაქვს, იპოვე ის runtime-ის შიგნით გაზომვით - heap-ის შეჯამება Java-ში, collectgarbage("count") Lua-ში, resource-ების მონიტორები FiveM-ში, handle-ების dump-ები SourceMod-ში - და შენი plugin-ების ნახევრების გათიშვით, სანამ დახრა არ გაქრება. სანამ გამოსწორდება, დაგეგმილი restart და ცოტა მარაგი არ აძლევს მას საშუალებას, პიკის დროს crash-ად იქცეს.
გაჟონვა, ზრდა თუ cache: სამი მსგავსი გრაფიკი#
პანელის მეხსიერების გრაფიკი სამი სრულიად განსხვავებული სიტუაციისთვის ერთსა და იმავე მზარდ ხაზს აჩვენებს. მათი გარჩევა მთელი პირველი ნაბიჯია.
| სქემა | რას ხედავ | რა არის | მოქმედება |
|---|---|---|---|
| შევსება და პლატო | restart-ის შემდეგ საათობით იზრდება, შემდეგ ბრტყელია | heap ან cache, რომელიც თავის ზომას აღწევს | არაფერი; გეგმა პლატოზე შეარჩიე |
| საფეხურებრივი ზრდა | იზრდება, როცა მოთამაშეები იკვლევენ ან აშენებენ, სხვა დროს ბრტყელია | სამყარო იზრდება | მოსალოდნელია; სამყაროს ლიმიტები ან მეტი მეხსიერება |
| მზარდი იატაკი | ყველაზე დაბალი წერტილი ყოველდღე უფრო მაღლაა, აქტივობის მიუხედავად | გაჟონვა | იპოვე; მანამდე გადატვირთე |
ტესტი ისაა, რომ გრაფიკი დღეების განმავლობაში წაიკითხო და არა წუთების, და ჩაღრმავებებს დააკვირდე - წყნარ საათებს, როცა არავინაა ონლაინ. ნამდვილი გაჟონვა ღამითაც კი აგრძელებს ზრდას, ან სულ მცირე არასდროს აბრუნებს იმას, რაც აიღო. ზრდა მოთამაშეების აქტივობას მიჰყვება: ღამე, როცა არავინ თამაშობს, ბრტყელი ღამეა. სავსე heap ერთხელ იზრდება და ჩერდება. სერვერის დატვირთვის გრაფიკის კითხვა იმავე გრაფიკზე სხვა ფორმებს განიხილავს.
ორი სისტემა პირველ სქემას გაჟონვას ამსგავსებს მათთვის, ვისაც მანამდე არ შეხვედრია.
- Java მეხსიერებას არ აბრუნებს. Minecraft-ის ან Project Zomboid-ის სერვერი, გაშვებული
-Xmx6G-ით, ადრე თუ გვიან 6 GB-მდე პლუს ცოტა დამატებითს გამოიყენებს და იქ დარჩება. garbage collector heap-ის შიგნით მეხსიერებას ათავისუფლებს; ოპერაციულ სისტემას მას იშვიათად უბრუნებს. ეს ნორმაა, და ამიტომ აფრთხილებს Minecraft-ის RAM-ის გზამკვლევი Java-სთვის ყველაფრის მიცემის წინააღმდეგ. - Unity-სა და Unreal-ის სერვერები heap-ებს საფეხურებად ზრდიან და ხელახლა იყენებენ. Valheim-ის ან Palworld-ის სერვერის მეხსიერება დატვირთული საღამოს შემდეგ უფრო მაღალია, ვიდრე წყნარის შემდეგ, და შეიძლება აღარ დაეცეს, ისე რომ არაფერი ჟონავდეს.
რატომ იქცევა გაჟონვა crash-ად#
გაჟონვა ნელი პრობლემაა მოულოდნელი დასასრულით. ყოველდღე იატაკი უფრო მაღლაა, პიკი უფრო მაღლაა, და რაღაც მომენტში დატვირთული საღამოს პიკი გეგმის მეხსიერების ლიმიტს ხვდება.
კონტეინერულ ჰოსტინგზე ორი შესაძლო დასასრულია. ორიგინალ Pterodactyl-ს შეუძლია სერვერს swap-ის გამოყენების უფლება მისცეს, რაც მას ცოცხალს ტოვებს, მაგრამ მტკივნეულად ნელს ხდის. RE:NODE-ზე kernel კონტეინერს ლიმიტზე აჩერებს და ის swap-ის ნაცვლად სუფთად გადაიტვირთება - სწრაფად აღდგება, მაგრამ თამაშის თვალსაზრისით შეუნახავი გაჩერებაა, ასე რომ ბოლო autosave-ის შემდეგ ყველაფერი იკარგება. crash-ების მეთვალყურეც ამჩნევს: ის ყოველ ორ წუთში ამოწმებს სერვერებს, რომელთა uptime უკან წავიდა, იგნორირებს შენ მიერ მოთხოვნილ restart-ებს, და საათში სამი მოუთხოვნელი restart-ის შემდეგ სერვერის გვერდზე გაფრთხილებას აქვეყნებს და ავტომატურად ხსნის ticket-ს. გაჟონვიანი სერვერი ამ სიხშირეს ჩვეულებრივ ვერ აღწევს, რადგან გაჟონვას თავიდან შევსებისთვის დღეები სჭირდება, მაგრამ გაჟონვიანი სერვერი, რომელიც თავის ჭერზე crash-loop-შია, მიაღწევს. დეტალები მოცემულია სტატიაში რატომ გადაიტვირთება შენი თამაშის სერვერი გამუდმებით, ხოლო kernel-ის მხარე - სტატიაში Linux-ის swap და OOM killer.
სწორად გაზომვა#
სანამ რამეს დაადანაშაულებ, აიღე რიცხვები.
- გადატვირთე სერვერი და ჩაიწერე მეხსიერება მას შემდეგ, რაც ჩატვირთვას დაასრულებს და დამშვიდდება - ხუთი ან ათი წუთი.
- იატაკი ყოველდღე ჩაიწერე სამიდან ხუთ დღემდე: ყველაზე დაბალი მაჩვენებელი წყნარ საათებში.
- ჩაიწერე, რა შეიცვალა ამ დღეებში: მოთამაშეები, ახალი შენობები, ახალი plugin-ები, ივენთები.
- გამოთვალე დახრა. დღეში 150 MB 6 GB-იან გეგმაზე თვეს გაძლევს; დღეში 800 MB - კვირას.
თუ მანქანაზე shell გაქვს, პროცესის რეზიდენტული მეხსიერება /proc-შია:
$ grep VmRSS /proc/$(pgrep -f valheim_server)/statusVmRSS: 3145728 kBერთი სიფრთხილე კონტეინერის მაჩვენებლებთან დაკავშირებით. კონტეინერის მეხსიერების აღრიცხვა შეიძლება ფაილების cache-საც მოიცავდეს - თამაშის ფაილების გვერდებს, რომლებიც kernel-მა მეხსიერებაში შეინახა, რადგან ახლახან წაიკითხეს. ეს cache დაბრუნებადია და გაჟონვა არ არის, თუმცა შეიძლება cgroup-ის ნედლ რიცხვებში გამოჩნდეს. პროცესის საკუთარი რეზიდენტული მეხსიერება, ან runtime-ის საკუთარი ანგარიში, უკეთესი საზომია.
პოვნა Java-ს სერვერებში#
Minecraft და Project Zomboid JVM-ზე მუშაობს, და Java-ს თამაშის ნებისმიერ runtime-ზე საუკეთესო ხელსაწყოები აქვს.
Java-ში კითხვა არ არის "ბევრს იყენებს თუ არა პროცესი" - ის თავის heap-ს გამოიყენებს - არამედ "იზრდება თუ არა ცოცხალი მონაცემები garbage collection-ის შემდეგ". Paper-ზე ამას spark თამაშის შიგნიდან პასუხობს:
/spark heapsummary/spark gcheapsummary ჩამოთვლის კლასებს, რომლებიც ყველაზე მეტ მეხსიერებას იკავებს, ეგზემპლარების რაოდენობითა და ზომით. ერთი restart-იდან მალევე გადაიღე, მეორე - დღის შემდეგ. კლასი, რომლის რაოდენობა ათასობიდან მილიონობამდე გაიზარდა და რომლის სახელშიც plugin-ის პაკეტის სახელია, შენი ეჭვმიტანილია. /spark gc garbage collection-ის ქცევას აჩვენებს; heap, რომელიც ყოველი collection-ის შემდეგ თითქმის სავსეა, ნიშნავს, რომ ცოცხალი ნაკრები გაიზარდა. გამონატანის კითხვას აღწერს spark profiler-ის გზამკვლევი.
თამაშის გარეთ იგივე საქმეს JDK-ის საკუთარი ხელსაწყოები აკეთებს:
$ jcmd $(pgrep -f paper.jar) GC.class_histogram | head -25$ jstat -gcutil $(pgrep -f paper.jar) 10sჰისტოგრამა იგივე ინფორმაციაა, რაც heapsummary. jstat -gcutil ყოველ ათ წამში ბეჭდავს, რამდენად სავსეა heap-ის თითოეული თაობა; დააკვირდი old generation-ის სვეტს სრული collection-ების შემდეგ მაშინვე. თუ ის საათების განმავლობაში ნელ-ნელა იზრდება, ცოცხალი მონაცემები იზრდება. სრული heap dump (jcmd <pid> GC.heap_dump /tmp/heap.hprof) შეიძლება Eclipse MAT-ში გახსნა, რომ ნახო, რა იჭერს ობიექტებს, მაგრამ ის სერვერს აჩერებს და heap-ის ზომის ფაილს წერს - წყნარ დროს გააკეთე.
Java-ში ჩვეულებრივი დამნაშავეები plugin-ებია, რომლებიც მოთამაშეებზე, სამყაროებზე ან chunk-ებზე მიმართვებს ინახავენ მას შემდეგ, რაც ისინი მეხსიერებიდან გაიტვირთება: თითო მოთამაშის map-ები, რომლებიც გასვლისას არასდროს სუფთავდება, listener, რომელიც ყოველ მოვლენას ინახავს, cache ზომის ლიმიტის გარეშე. chunk-ების ჩამტვირთავი plugin-ები და სამყაროს რუკის ზოგი renderer მეხსიერებაში ინახავს chunk-ებს, რომლებსაც სერვერი სხვაგვარად გაიტვირთავდა. ისინი ჰისტოგრამაში ჩანს, როგორც მზარდი სამყაროს ან chunk-ის ობიექტები.
პოვნა Lua-ში: Garry's Mod, FiveM, Don't Starve Together#
Lua საკუთარ heap-ის ზომას გეუბნება. Garry's Mod-ში, სერვერის კონსოლიდან:
lua_run print(collectgarbage("count"))რიცხვი სერვერის Lua state-ის მიერ გამოყენებული კილობაიტებია. ჩაიწერე ის restart-ის შემდეგ, რამდენიმე საათის შემდეგ და დღის შემდეგ. თუ ის თანდათან იზრდება, addon ინახავს ცხრილებს, რომლებსაც არასდროს ათავისუფლებს - ხშირად თითო მოთამაშის ცხრილებს მოთამაშის entity-ით, ტაიმერებს, რომლებიც ყოველ spawn-ზე იქმნება და არასდროს იშლება, ან განმეორებით დამატებულ hook-ებს. addon-ების ნახევრებად ამოღება და ამ რიცხვზე რამდენიმე საათის დაკვირვება მიზეზს გაცილებით სწრაფად ავიწროებს, ვიდრე პროცესის საერთო მეხსიერებაზე დაკვირვება.
FiveM მეხსიერებას resource-ების მიხედვით აჩვენებს. კლიენტის F8 კონსოლში resmon 1 ხსნის resource-ების მონიტორს, CPU-ის დროითა და მეხსიერებით თითოეული გაშვებული resource-ისთვის, როგორც მას ეს კლიენტი ხედავს, ხოლო სერვერის საკუთარი profiler ბრძანება სერვერის მხარის resource-ების ფასს იწერს. resource, რომლის მეხსიერების სვეტი თანდათან იზრდება, როცა სხვები ადგილზე დგანან, გაჟონვაა. მის კითხვას აღწერს FiveM-ის სერვერის წარმადობა და resmon.
Don't Starve Together და Lua-თი მოდიანი სხვა თამაშები იმავე პრინციპს მიჰყვება: Lua-ს მეხსიერება მოდებს ეკუთვნის, ასე რომ გაჟონვა თითქმის ყოველთვის მოდია, რომელიც მდგომარეობას თითო მოთამაშეზე, თითო entity-ზე ან თითო დღეზე ინახავს და არასდროს ასუფთავებს.
პოვნა Unity-ის, Unreal-ისა და Source-ის სერვერებში#
Unity-ის თამაშები
Valheim, Rust, 7 Days to Die და Unturned Unity-ზე მუშაობს, მოდებისთვის ჩვეულებრივ Mono runtime-ით. მათი managed heap იზრდება და იშვიათად მცირდება, ამიტომ მაღალი, მაგრამ ბრტყელი რიცხვი ნორმაა. აქ გაჟონვები ძირითადად მოდებიდან მოდის: BepInEx plugin Valheim-ში, Oxide-ის ან Carbon-ის plugin Rust-ში, Harmony მოდი 7 Days to Die-ში. Rust კონსოლში რამდენიმე ხელსაწყოს გაძლევს - gc.collect collection-ს აიძულებს, ხოლო plugin-ების framework-ები თითო plugin-ის hook-ების დროებს აჩვენებს, რაც ცუდად მომუშავე plugin-ებზე მიუთითებს - მაგრამ თითო plugin-ის მეხსიერების აღრიცხვა შეზღუდულია. საიმედო მეთოდი შუაზე გაყოფაა.
ზოგ Unity თამაშს ასევე აქვს სამყაროთი გამოწვეული ზრდა, რომელიც გაჟონვას ჰგავს: Rust-ის entity-ების რაოდენობა wipe-ის განმავლობაში იზრდება, როცა მოთამაშეები აშენებენ, ხოლო 7 Days to Die-ის მეხსიერება ჩატვირთული რეგიონების რაოდენობასთან ერთად იზრდება. სანამ მოდს დაადანაშაულებ, შეადარე entity-ების ან რეგიონების რაოდენობებს.
Unreal Engine-ის თამაშები
Palworld, ARK და Unreal-ის სხვა სერვერები ნატიურ მეხსიერებას მართავენ. აქ გაჟონვები ჩვეულებრივ თავად თამაშშია და არა მოდებში, და ერთადერთი ნამდვილი გამოსავალი დეველოპერის patch-ებია. Palworld-ის ადრეული რელიზები ცნობილი მაგალითი იყო, მეხსიერებით, რომელიც მუშაობის დროსთან ერთად restart-მდე იზრდებოდა. თუ შენი Unreal სერვერი მოდების გარეშე მზარდ იატაკს აჩვენებს, შუაზე გაყოფაზე დროის დახარჯვამდე შეამოწმე დეველოპერის patch notes და საზოგადოების შეტყობინებები, და შეაკავე restart-ებით. კონკრეტულად ამ თამაშს აღწერს Palworld-ის სერვერის მეხსიერება.
Source და SourceMod
SourceMod handle-ებს აკონტროლებს - მიმართვებს, რომლებსაც plugin-ები ტაიმერებზე, ფაილებზე, ბაზის მოთხოვნებსა და მონაცემთა სტრუქტურებზე ინახავენ. plugin, რომელიც handle-ებს ხსნის და არასდროს ხურავს, ჟონავს, და SourceMod ამას ზღვარზე ამჩნევს, შეცდომების ლოგში წერს MEMORY LEAK DETECTED IN PLUGIN-ს plugin-ის ფაილის სახელით და შემდეგ plugin-ს გამორთავს. handle-ების რაოდენობის პირდაპირ შემოწმებაც შეგიძლია:
sm_dump_handles handles.txtdump ჩამოთვლის handle-ებს მფლობელი plugin-ის მიხედვით. ერთი restart-ის შემდეგ გადაიღე, მეორე - დღის შემდეგ; plugin, რომლის რაოდენობაც მუდმივად იზრდება, გაჟონვაა. plugin-ების მართვას აღწერს Team Fortress 2-ის SourceMod plugin-ები.
შუაზე გაყოფა, როცა ხელსაწყოები არაფერზე მიუთითებს#
როცა runtime-ს მეხსიერების კომპონენტზე მიკუთვნება არ შეუძლია, გაყავი და გაზომე:
- გააკეთე backup სერვერის, კონფიგებისა და plugin-ების მონაცემების.
- გათიშე plugin-ების ან მოდების ნახევარი, რომელთა ამოღებაც save-ების გაფუჭების გარეშე უსაფრთხოა. კონტენტის მოდები, რომლებზეც სამყაროა დამოკიდებული, დატოვე; ისინი ბოლოს შეამოწმე, ასლზე.
- ამუშავე სრული დღე და ჩაიწერე იატაკი იმავე წყნარ საათზე.
- თუ დახრა გაქრა, გაჟონვა იმ ნახევარშია, რომელიც ამოიღე; ის ნახევარი აღადგინე და მეორე ამოიღე. თუ დარჩა, ის ჯერ კიდევ გაშვებულ ნახევარშია.
- გაიმეორე, სანამ ერთი plugin არ დარჩება. ოც plugin-ს დაახლოებით ხუთი რაუნდი სჭირდება - სამუშაო კვირა, რადგან ყოველ ტესტს დღე სჭირდება.
ეს ნელია, ამიტომ შეძლებისდაგვარად ასლზე გააკეთე. ასეთის შენახვას აღწერს სატესტო სერვერი production-ის გვერდით, ხოლო რა ქნა, როცა მოდის განახლება ყველაფერს აფუჭებს იმავე განახევრების მეთოდს crash-ებზე იყენებს. როცა დამნაშავეს იპოვი, შეამოწმე განახლება, ავტორს შეატყობინე შენი "მანამდე და მერე" რიცხვებით, ან ჩაანაცვლე.
შეკავება გამოსწორებამდე#
გაჟონვების უმეტესობას შენ ვერ ასწორებ; მათ plugin-ის ავტორი ან თამაშის დეველოპერი ასწორებს, ადრე თუ გვიან. მანამდე:
- დაგეგმე restart, სანამ იატაკი საშიშ ზღვარს მიაღწევს. თუ იატაკი დღეში 500 MB-ით იზრდება და ჩვეულებრივ პიკზე 2 GB მარაგი გაქვს, restart ყოველ ორ დღეში საკმარისია - ყოველღამე არ გჭირდება. Restart-ის განრიგები, რომლებიც შველის ხსნის, როგორ აირჩიო რიტმი გრაფიკით და არა ჩვევით.
- შეინახე ყოველი restart-ის წინ. იმავე განრიგში თამაშის save ბრძანება restart-მდე ერთი წუთით ადრე ჩასვი. restart, რომელიც ჯერ ინახავს, არავის არაფერი უჯდება.
- შეამოკლე autosave-ის ინტერვალი, სანამ გაჟონვა არსებობს, რომ დაუგეგმავმა გაჩერებამ ჭერზე წუთები დაკარგოს. თითოეული თამაშის პარამეტრები მოცემულია სტატიაში თამაშის სერვერის autosave-ის ინტერვალები.
- შეინარჩუნე მარაგი. სერვერს, რომლის ჩვეულებრივი პიკი ლიმიტიდან რამდენიმე ასეულ მეგაბაიტშია, გაჟონვისთვის მარაგი არ აქვს. RE:NODE-ზე უფრო მაღალ საფეხურზე გადასვლა ლიმიტს უკვე არსებულ სერვერზე ცვლის, თავიდან აწყობის გარეშე.
RE:NODE-ზე Schedules ჩანართი cron გამოსახულებით უშვებს დალაგებულ ამოცანებს დაყოვნებებით - კონსოლის ბრძანება, შემდეგ ჩართვა-გამორთვის მოქმედება - ასე რომ "შეინახე, დაელოდე წუთს, გადატვირთე" ყოველ მეორე ღამეს 05:00-ზე ერთი განრიგია.
FAQ#
ნორმალურია, რომ ჩემი სერვერის RAM-ი გამუდმებით იზრდება?
restart-იდან რამდენიმე საათის განმავლობაში, კი - heap-ები და cache-ები ივსება. ამის შემდეგ მეხსიერება მოთამაშეების აქტივობასა და სამყაროს ზომას უნდა მიჰყვებოდეს. იატაკი, რომელიც ყოველდღე იზრდება, მაშინაც კი, როცა არავინ თამაშობს, გაჟონვაა.
რატომ იყენებს ჩემი Minecraft-ის სერვერი მთელ RAM-ს, რაც მივეცი?
რადგან Java თავის heap-ს -Xmx-მდე ავსებს და მეხსიერებას ოპერაციულ სისტემას იშვიათად უბრუნებს. ეს გაჟონვა არ არის. სანამ შეწუხდები, შეამოწმე, იზრდება თუ არა heap-ის ცოცხალი მონაცემები garbage collection-ის შემდეგ, spark-ის heapsummary-ით ან jstat-ით.
ყოველდღიური restart მეხსიერების გაჟონვას გამოასწორებს?
შეაკავებს, მაგრამ არ გამოასწორებს. გაჟონვა პროცესთან ერთად ნულდება და თავიდან იწყება. გამოიყენე restart, რომ სერვერი ლიმიტისგან შორს დაიჭირო, სანამ მიზეზს იპოვი და მოაშორებ.
შეუძლია მეტ RAM-ს მეხსიერების გაჟონვის მოგვარება?
ის დროს იყიდის, დამატებითი მეხსიერების დღიურ ზრდაზე გაყოფის პროპორციით. ეს შეიძლება სწორი მოკლევადიანი პასუხი იყოს, მაგრამ გაჟონვა მაინც მიაღწევს ახალ ლიმიტს, თუ რამე სერვერს მანამდე არ გადატვირთავს.
როგორ ვიპოვო, რომელი plugin ჟონავს მეხსიერებას?
ჯერ runtime-ის საკუთარი ხელსაწყოები გამოიყენე - spark ან jcmd Java-სთვის, collectgarbage("count") Lua-სთვის, resmon FiveM-ისთვის, sm_dump_handles SourceMod-ისთვის. თუ ისინი ერთ plugin-ზე არ მიუთითებს, plugin-ები ნახევრებად გათიშე და მეხსიერების იატაკი ყოველ ჯერზე დღის განმავლობაში გაზომე.




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