Bu, Java ile uzun süredir devam eden bir şikayetti, ancak büyük ölçüde anlamsız ve genellikle yanlış bilgilere bakmaya dayanıyor. Her zamanki ifade "Java'da Merhaba Dünya 10 megabayt alır! Neden buna ihtiyacı var?" 64 bit JVM'de Hello World'ü 4 gigabaytın üzerinde bir iddiada bulunmanın bir yolu var ... en azından bir ölçüm biçimiyle.
java -Xms1024m -Xmx4096m com.example.Hello
Belleği Ölçmenin Farklı Yolları
Linux'ta, üst komut size bellek için birkaç farklı sayı verir. Merhaba Dünya örneği hakkında şöyle diyor:
PID KULLANICI PR NI VIRT RES SHR S% CPU% MEM ZAMANI + KOMUT
2120 kilogramlık 20 0 4373m 15m 7152 S 0 0.2 0: 00.10 java
- VIRT sanal bellek alanıdır: sanal bellek haritasındaki her şeyin toplamı (aşağıya bakınız). Olmadığı durumlar dışında büyük ölçüde anlamsızdır (aşağıya bakınız).
- RES yerleşik ayarlanan boyuttur: RAM'de mevcut olan sayfa sayısı. Hemen hemen her durumda, "çok büyük" derken kullanmanız gereken tek sayı budur. Ancak özellikle Java hakkında konuşurken hala çok iyi bir sayı değil.
- SHR, diğer işlemlerle paylaşılan yerleşik bellek miktarıdır. Java işlemi için bu genellikle paylaşılan kitaplıklar ve bellek eşlemeli JAR dosyaları ile sınırlıdır. Bu örnekte, yalnızca bir Java işlemi çalışıyordu, bu yüzden 7k'nin işletim sistemi tarafından kullanılan kitaplıklardan kaynaklandığından şüpheleniyorum.
- SWAP varsayılan olarak açık değildir ve burada gösterilmez. Takas alanında olsun ya da olmasın, o anda diskte yerleşik olan sanal bellek miktarını gösterir . İşletim sistemi, aktif sayfaları RAM'de tutmak konusunda çok iyidir ve takas için tek tedavi (1) daha fazla bellek satın almak veya (2) işlem sayısını azaltmaktır, bu nedenle bu sayıyı göz ardı etmek en iyisidir.
Windows Görev Yöneticisi'nin durumu biraz daha karmaşıktır. Windows XP'de "Bellek Kullanımı" ve "Sanal Bellek Boyutu" sütunları vardır, ancak resmi belgeler ne anlama geldiklerine sessizdir. Windows Vista ve Windows 7 daha fazla sütun ekler ve bunlar aslında belgelenir . Bunlardan "Çalışma Seti" ölçümü en yararlı olanıdır; kabaca Linux'taki RES ve SHR toplamına karşılık gelir.
Sanal Bellek Haritasını Anlama
Bir işlem tarafından tüketilen sanal bellek, işlem belleği haritasında bulunan her şeyin toplamıdır. Buna veriler (ör. Java yığını), ancak program tarafından kullanılan tüm paylaşılan kitaplıklar ve bellek eşlemeli dosyalar da dahildir. Linux'ta, işlem alanına eşlenen tüm şeyleri görmek için pmap komutunu kullanabilirsiniz (buradan sonra sadece Linux'a başvuracağım çünkü kullandığım şey bu; Pencereler). İşte "Merhaba Dünya" programının hafıza haritasından bir alıntı; tüm hafıza haritası 100 satırdan daha uzun ve bin satırlı bir listeye sahip olmak olağandışı değil.
0000000040000000 36K rx-- /usr/local/java/jdk-1.6-x64/bin/java
0000000040108000 8K rwx-- /usr/local/java/jdk-1.6-x64/bin/java
0000000040eba000 676K rwx - [anon]
00000006fae00000 21248K rwx - [anon]
00000006fc2c0000 62720K rwx - [anon]
0000000700000000 699072K rwx - [anon]
000000072aab0000 2097152K rwx - [anon]
00000007aaab0000 349504K rwx - [anon]
00000007c0000000 1048576K rwx - [anon]
...
00007fa1ed00d000 1652K r-xs- /usr/local/java/jdk-1.6-x64/jre/lib/rt.jar
...
00007fa1ed1d3000 1024K rwx - [anon]
00007fa1ed2d3000 4K ----- [anon]
00007fa1ed2d4000 1024K rwx - [anon]
00007fa1ed3d4000 4K ----- [anon]
...
00007fa1f20d3000 164K rx-- /usr/local/java/jdk-1.6-x64/jre/lib/amd64/libjava.so
00007fa1f20fc000 1020K ----- /usr/local/java/jdk-1.6-x64/jre/lib/amd64/libjava.so
00007fa1f21fb000 28K rwx-- /usr/local/java/jdk-1.6-x64/jre/lib/amd64/libjava.so
...
00007fa1f34aa000 1576K rx-- /lib/x86_64-linux-gnu/libc-2.13.so
00007fa1f3634000 2044K ----- /lib/x86_64-linux-gnu/libc-2.13.so
00007fa1f3833000 16K rx-- /lib/x86_64-linux-gnu/libc-2.13.so
00007fa1f3837000 4K rwx-- /lib/x86_64-linux-gnu/libc-2.13.so
...
Biçimin hızlı bir açıklaması: her satır, segmentin sanal bellek adresiyle başlar. Bunu segment boyutu, izinler ve segmentin kaynağı izler. Bu son öğe, mmap yoluyla ayrılan bir bellek bloğunu gösteren bir dosya veya "anon" dur .
Yukarıdan başlayarak,
- JVM yükleyici (yani, yazarken çalıştırılan program
java
). Bu çok küçük; tüm yaptığı gerçek JVM kodunun saklandığı paylaşılan kütüphanelere yüklenmektir.
- Java yığınını ve dahili verileri tutan bir grup anon bloğu. Bu bir Sun JVM'dir, bu nedenle yığın, her biri kendi bellek bloğu olan birden fazla nesle bölünür. JVM'nin
-Xmx
değere bağlı olarak sanal bellek alanı ayırdığını unutmayın ; bu onun bitişik bir yığın olmasını sağlar. -Xms
Değeri programı başlar ve tetik çöp toplama için bu sınır yaklaşılırken zaman "kullanımda" ne kadar yığın söylemek dahili olarak kullanılır.
- Bellek eşlemeli JARfile, bu durumda "JDK sınıflarını" tutan dosya. Bir JAR'ı bellek eşlediğinizde, içindeki dosyalara çok verimli bir şekilde erişebilirsiniz (her seferinde başlangıçtan okumak yerine). Sun JVM, sınıf yolundaki tüm JAR'ları bellekle eşler; uygulama kodunuzun bir JAR'a erişmesi gerekiyorsa, bunu bellek eşlemesiyle de eşleştirebilirsiniz.
- İki iş parçacığı için iş parçacığı başına veri. 1M bloğu iplik yığınıdır. 4k bloğu için iyi bir açıklamam yoktu, ancak @ericsoe bunu bir "koruma bloğu" olarak tanımladı: okuma / yazma izinleri yok, bu yüzden erişilirse bir segment hatasına neden olur ve JVM bunu yakalar ve çevirir a
StackOverFlowError
. Gerçek bir uygulama için, bu girişlerin yüzlerce değilse bellek haritasında tekrar tekrar onlarca göreceksiniz.
- Gerçek JVM kodunu barındıran paylaşılan kitaplıklardan biri. Bunlardan birkaç tane var.
- C standart kitaplığı için paylaşılan kitaplık. Bu, JVM'nin kesinlikle Java'nın bir parçası olmayan yüklerinden sadece bir tanesidir.
Paylaşılan kitaplıklar özellikle ilginçtir: her paylaşılan kitaplık en az iki bölümden oluşur: kitaplık kodunu içeren salt okunur bir segment ve kitaplık için global işlem başına veriler içeren bir okuma-yazma segmenti (ne olduğunu bilmiyorum izinsiz segment; sadece x64 Linux'ta gördüm). Kütüphanenin salt okunur kısmı, kütüphaneyi kullanan tüm işlemler arasında paylaşılabilir; örneğin, libc
paylaşılabilen 1,5M sanal bellek alanına sahiptir.
Sanal Bellek Boyutu Ne Zaman Önemli?
Sanal bellek haritası birçok şey içeriyor. Bazıları salt okunurdur, bazıları paylaşılır ve bazıları tahsis edilir, ancak hiçbir zaman dokunulmaz (örneğin, bu örnekteki 4Gb yığınının neredeyse tamamı). Ancak işletim sistemi yalnızca ihtiyaç duyduğu şeyleri yükleyecek kadar akıllıdır, bu nedenle sanal bellek boyutu büyük ölçüde önemsizdir.
Sanal bellek boyutunun önemli olduğu yer, 32 bit işletim sisteminde çalışıyorsanız, yalnızca 2Gb (veya bazı durumlarda 3Gb) işlem adres alanı ayırabileceğiniz yerdir. Bu durumda, kıt bir kaynakla uğraşıyorsunuz ve büyük bir dosyayı bellekle eşleştirmek veya çok sayıda iş parçacığı oluşturmak için yığın boyutunuzu küçültmek gibi denemeler yapmak zorunda kalabilirsiniz.
Ancak, 64 bit makinelerin her yerde bulunduğu göz önüne alındığında, Sanal Bellek Boyutunun tamamen alakasız bir istatistik olması çok uzun süreceğini düşünmüyorum.
Yerleşik Set Boyutu ne zaman önemlidir?
Yerleşik Ayar boyutu, sanal bellek alanının gerçekte RAM olan bölümüdür. RSS'niz toplam fiziksel belleğinizin önemli bir parçası haline gelirse endişelenmeye başlamanın zamanı gelmiş olabilir. RSS'niz tüm fiziksel belleğinizi alacak şekilde büyürse ve sisteminiz değişmeye başlarsa, endişelenmeye başlamanın zamanı gelmiş demektir.
Ancak RSS, özellikle hafif yüklü bir makinede de yanıltıcıdır. İşletim sistemi, bir işlem tarafından kullanılan sayfaları geri almak için fazla çaba harcamaz. Bunu yaparak kazanılacak çok az fayda vardır ve işlem gelecekte sayfaya dokunursa pahalı bir sayfa hatası potansiyeli vardır. Sonuç olarak, RSS istatistiği aktif kullanımda olmayan çok sayıda sayfa içerebilir.
Sonuç olarak
Değiştirmediğiniz sürece, çeşitli bellek istatistiklerinin size ne söylediği konusunda aşırı endişelenmeyin. Sürekli büyüyen bir RSS'nin bir tür bellek sızıntısına işaret edebileceği uyarısı ile.
Bir Java programı ile, yığınta neler olduğuna dikkat etmek çok daha önemlidir. Tüketilen toplam alan miktarı önemlidir ve bunu azaltmak için atabileceğiniz bazı adımlar vardır. Daha da önemlisi, çöp toplamada harcadığınız zaman miktarı ve yığının hangi bölümlerinin toplandığıdır.
Diske (yani bir veritabanına) erişmek pahalıdır ve bellek ucuzdur. Birini diğeriyle takas edebiliyorsanız bunu yapın.