G1 etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster
G1 etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster

28 Mayıs 2019 Salı

G1 (Garbage-First) Garbage Collector - Java 9 ile Default Oldu

Giriş
G1 uzun bir aradan sonra çıkan bir garbage collector. Açıklaması şöyle.
Serial GC was the first garbage collector introduced in HotSpot in 1999 as part of Java Development Kit (JDK) 1.3.1. The Parallel and Concurrent Mark Sweep collectors were introduced in 2002 as part of JDK 1.4.2.
Ne Zaman Kullanılır
Açıklaması şöyle. 6GB'den büyük heap varsa kullanılır. Java 15'ten sonra Z GC kullanılıyor
The G1 Garbage Collector is a heavy duty GC designed for big workloads on machines with large heap sizes (roughly 6GB or higher). It tries to adapt to the working conditions in the given machine. You can explicitly enable it using the JVM option -XX:+UseG1GC.

G1 is a concurrent GC that works in the background and minimizes pauses. One of it’s cooler features is string de-duplication which reduces the overhead of strings in RAM. You can activate that feature using -XX:+UseStringDeduplication.
Bir diğer açıklama şöyle. Yani G1 GC daha fazla işlemci kullanıyor. Dolayısıyla G1 GC çok işlemcili ve yüksek miktarda belleğe sahip bilgisayarlar için tasarlanmış ve concurrent olarak çalışıyor.
Since Java 9, the G1 collector has been the default GC, the main idea being to slice up GC pauses according to user-supplied time targets. It usually offers shorter pause times but at the expense of lesser throughput. In addition, the pause time increases with the size of the heap.
Java 8 İle Geliyor, Java 9 ile Default Oldu
G1 kelimesi Garbage-First anlamına gelir. G1 Garbage Collector' ın bundan önceki Serial, Parallel ve CMS (Concurrent Mark And Sweep) garbage collector'lardan daha iyi olduğu söyleniyor. Sebebi ise daha tahmin edilebilir duraklama/gecikme (predictable latency) süresi. Açıklaması şöyle. Yani örneğin en fazla 10 ms Garbage Collection süresi istiyorsak bu süreyi tutturma olasılığımız diğer GC yöntemlerine göre daha yüksek bir ihtimal. 
The improvement over the CMS collector is called the G1 collector. Instead of having specific young and old generations for Heap this collector uses its entire heap and divides it into multiple regions. It has more footprint and the advantage of this collector is it has the most predictable latency and this is the best feature of this collector. When we start our application, we can pass on this variable that the maximum pause time (maxTargetPauseTime) that our application can withstand say 10ms for example. The G1 collector will try to ensure that the Garbage collection is done only for 10 ms and even if there is some garbage left it will take care in the next cycle. If we want the predictable latencies and pause times the G1 collector will be the best collector to use. This is the most commonly used collector for all the performance testing needs.
Java 10 ile İyileştirme - Parallel Full GC for G1 (JEP 307)
Açıklaması şöyle. Yani Full GC başlayınca artık paralel olarak yapılıyor.
Another interesting feature which improves G1 worst-case latencies by making the full GC parallel.

If you remember, In Java 9 release, G1 was made the default GC for JVM, which was designed to avoid full GC, but when the concurrent collections couldn’t reclaim memory quick enough it would end up falling back on a full GC, and that creates a problem.

This change will parallelize the full GC algorithm so that in the unlikely event of a G1 Full GC, same number of threads can be used as in the concurrent collections to improve the overall performance.
Bir başka açıklama şöyle
Till Java 10, G1 garbage collector was being used as primary concurrent garbage collector. Java 10 offered parallel full GC for garbage-first (G1) GCs, improving its worst-case latency. It also improved source code isolation of multiple GCs for the GC code in HotSpot, introducing the GC interface. But, all these changes didn’t resolve the most important issues like long GC pauses with Garbage collector in Java.
Ancak
Her iki iyileştirme de stop-the-world durumunda istenilen şeyi tam olarak sağlamadı. Bu yüzden Java 11 ile ZGC geldi.

Region Nedir?
Açıklaması şöyle
By design, the G1 garbage collector manages the heap by dividing it into a fixed number of same-size regions. By default, the maximum number of regions is 2048, and region size corresponds to the maximum heap size as follows: heap size < 4GB: 2MB, <8GB: 4MB, <16GB: 8MB, and so on. Normally objects are allocated into a given region until it's full, and then at some point, the GC frees up the entire region by evacuating all live objects from it.
Şeklen şöyle

Region dizilimi de şeklen şöyledir. Burada Eden + Survivor alanları Young Generation olarak bilinir. Old Generation mavi renkte. Humongous nesneler Free Space olarak tanımlanan gri renkli alanlara yerleşirler.


Burada önemli olan şey gri renkteki Free Space alanların gerektiğinde Eden veya Survivor veya Old Generation veya Humongous alan olarak kullanılabilmesi. Ayrıca aynı tipteki region'ların ardışık/yan yana olması gerekmiyor. Açıklaması şöyle
The heap is partitioned into a set of equal-sized heap regions, each a contiguous range of virtual memory. Certain region sets are assigned the same roles (eden, survivor, old) as in the older collectors, but there is not a fixed size for them. This provides greater flexibility in memory usage.
Eskiden bu alanların büyüklüğü sabitti. Şeklen şöyleydi.


Humongous Object Nedir
Açıklaması şöyle. Region büyüklüğünün yarısına eşit veya daha büyük nesne Humongous nesne kabul edilir.
All these changes, however, if an object (typically an array) is bigger than half the region size. Such objects are called Humongous in G1 terminology, and are handled as follows:

1. A humongous object is allocated directly in the Old Generation (note that this may or may not the case in JDK 11 and newer).

2. A separate humongous region is created for each such object by concatenating several contiguous regions. Some of these regions may need to be GCed first.

3. Each humongous region can accommodate only one Humongous object and nothing else. That is, the space between the end of the humongous object and the end of the humongous region (which in the worst case can be close to half the normal region size) is unused (this is true at least in JDK 8-11, but may be addressed in latest JDK versions) 
Humongous Object Neden Zararlı?
1. Humongous Object yerleştiği Region'ın geri kalanının kullanılmasını engelliyor. Bu yüzden heap içinde parçalanmaya sebep oluyor. Açıklaması şöyle
If the regions contain humongous objects, space between the last humongous object in the region and the end of the region will be unused. For example, our 550KB object prevents any other objects from being stored in the 1MB region, meaning 450KB remains unused. When we have lots of these objects, it results in a great deal of unused space and creates fragmentation.
Örnek
Açıklaması şöyle
If allocated in the Old Gen, they cannot be GCed quickly even if they are short-lived (the Old Gen is collected less frequently than the Young Gen, and it takes more time)
Creating a humongous region out of several normal regions may need a non-trivial amount of time
If there are many humongous objects on the heap, it can lead to heap fragmentation because of unused "gaps" in humongous regions.
2. Eski bir JVM kullanıyorsak ve sürekli Humongous nesne yaratırsak bir zaman sonra Free Space biter ve mecburen Full GC yapmak zorunda kalırız. Bir başka açıklama şöyle
Until Java 1.8u40, the releasing of humongous regions was only done during full GC events.
Humongous nesneden dolayı sürekli Full GC yapılmasını açıklayan bir yaz burada. Daha sonra bu durum düzeltildi. Açıklaması şöyle  
We have Java less than Oracle JDK 8u45 version. For Java greater than this, it is written in the release notes that these humongous objects also get collected in minor events.

Search for: “G1 now collects unreachable Humongous objects during young collections” in the Release Notes


G1 Seçenekleri
G1 için tüm seçenekler burada.

Seçenekler üç başlık altında toplanabilir.
- GC logging options - G1 Garbage Collector - Loglama Seçenekleri yazısına taşıdım
- JVM sizing options - Seçenekler burada.
- G1C options - G1 Garbage Collector Seçenekleri yazısına taşıdım.

Seçenek Sayısı
Tablo şöyle. GC CMS'e göre daha az seçenek kullanıyor yani daha basit
GC Algorithm   JVM arguments(approximately)
Common to all 50
Parallel      6
CMS            72
G1            26
ZGC            8
Açıklaması şöyle.
One of the main design points for G1 was to simplify the tuning required to realize good performance. For instance, the major inputs into G1 are the initial and maximum Java heap size it can use, and a maximum GC pause time you are willing to tolerate. From there G1 will attempt to adaptively adjust to meet those inputs while it executes your application.
G1 Kullanıldığını Teyit Etmek
Şöyle yaparız.
java -XX:+PrintFlagsFinal -version |
grep -E "MAX|UseSerialGC|UseG1GC|MaxHeapSize)"
Çıktı olarak şunu alırız.
size_t MaxHeapSize    = 268435456     {product} {ergonomic}
bool UseG1GC        = true         {product} {default}
bool UseSerialGC    = false          {product} {ergonomic}
XX:+UseG1GC seçeneği
Use the Garbage First (G1) Collector
Eskiden olduğu gibi +UseXXXGC şeklindeki kullanım devam ediyor. Şöyle yaparız.
-XX:+AlwaysPreTouch 
-XX:CICompilerCount=15 
-XX:ConcGCThreads=6 
-XX:G1HeapRegionSize=16777216 
-XX:GCLogFileSize=10485760 
-XX:+HeapDumpOnOutOfMemoryError 
-XX:InitialHeapSize=48318382080 
-XX:MarkStackSize=4194304 
-XX:MaxHeapSize=48318382080 
-XX:MaxNewSize=28991029248 
-XX:MinHeapDeltaBytes=16777216 
-XX:NumberOfGCLogFiles=5 
-XX:-OmitStackTraceInFastThrow 
-XX:OnOutOfMemoryError=/bin/kill -9 %p 
-XX:+PrintGC -XX:+PrintGCApplicationStoppedTime 
-XX:+PrintGCDateStamps 
-XX:+PrintGCDetails 
-XX:+PrintGCTimeStamps 
-XX:+PrintTenuringDistribution 
-XX:+UseG1GC <------------------------------------Burası 
-XX:+UseGCLogFileRotation
Young GC Safhası
Bu safhada "Stop the World" yaklaşımı ile her şey durdurulur. MaxGCPauseMillis değeri dikkate alınarak şu iş yapılır. Bu işleri yapmak için çok sayıda thread kullanılır.

1. Hayatta kalan nesneler Survivor alanına taşınır.
2. Yaşlılık kriterini geçen bazı nesneler ise Old Generation alanına taşınır.

Old GC Safhası
Bu safha daha fazla adımdan oluşuyor. Tüm adımlar burada. Bu safhada da "Stop the World" yaklaşımı ile her şey durdurulur.


25 Şubat 2019 Pazartesi

G1 Garbage Collector - Loglama Seçenekleri

Giriş
Java 9 ile Unified JVM Logging seçeneği olarak sadece -XLog:gc kullanılıyor. Geri kalanlar kaldırıldı. Açıklaması şöyle
Unified JVM Logging got introduced in Java SE 9 with the motivation of simplifying root-cause analysis by allowing fine-grained and easy-to-configure logging options. 
GC çıktısını etkinleştirmek performans kaybına sebep olmuyor. Bu logları incelemek için bir araç kullanmak gerekir. Açıklaması şöyle
Thus, to analyze GC logs, it’s highly recommended to use GC log analysis tools such as GCeasy, HPJmeter. These tools parse GC logs and generate great graphical visualizations of data, reports Key Performance Indicators and several other useful metrics.
Kullanım
Şöyle yaparız
Until Java 8:

Below is the system property that is supported by all versions of Java until JDK 8.

-XX:+PrintGCDetails -Xloggc:<gc-log-file-path>

Example:
-XX:+PrintGCDetails -Xloggc:/opt/tmp/myapp-gc.log

From Java 9:

Below is the system property that is supported by all versions of Java starting with 
JDK 9.

-Xlog:gc*:file=<gc-log-file-path>

Example:
-Xlog:gc*:file=/opt/tmp/myapp-gc.log

1. -Xlog:gc seçeneği
Örnek
Şöyle yaparız.
-Xlog:gc=debug:file=/tmp/gc.log
Loglardaki humongous allocation isteklerini bulmak için şöyle yaparız
# humongous allocation için
grep "source: concurrent humongous allocation" /tmp/gc.log | 
  sed 's/.*allocation request: \([0-9]*\) bytes.*/\1/' > humongous_size.txt

# Daha sonra toplamı bulmak için
awk -F',' '{sum+=$1} END{print sum;}' humoungous_size.txt

2. -Xloggc seçeneği - Kullanmayın
Bu seçenek sanırım HotSpot'a mahsustu ve Java 9 ile deprecate edildi. Dosya ismine pattern verilebilir.
Örnek
Şöyle yaparız.
"-Xloggc:/home/GCEASY/gc-%t.log"
Örnek - filecount + filesize
Şöyle yaparız
-Xlog:gc*:gc.log:time,level,tags:filecount=5,filesize=1024K
Dosyada çıktı olarak şöyle bir şey görürüz
Pause Young (Normal) (G1 Evacuation Pause) 10247M->2108M(16384M) 9.477ms
Açıklaması şöyle. Burada Java uygulaması 16GB heap ile başlatılıyor ancak 2.1GB'ye gelince GC başlıyor.
In this case, the -Xmx parameter was 16G, this is where the 16484M comes from. Garbage collection started running when 10247 MB were used, and ended up with 2108 MB after the garbage collection. Which means that you absolutely can't go below ~ 2 GB because that's what Java really needs, and shouldn't go below around double this value.
Örnek
Şöyle yaparız.
java ... -Xlog:gc*=info,heap*=debug,safepoint=info:file=/home/logs/gc.log::filecount=3,
filesize=1m FooApp
3. Java 8 ve Öncesinde Loglama Seçenekleri
Loglamayla ilgili çoğu seçenek PrintXXX şeklinde.

Bir sürü seçenek var ancak hepsini kullanmak anlamlı değil sanırım.

-XX:+PrintGCDetails  seçeneği
Prints G1 Phases
Java 8 ve öncesinde şöyle yaparız.
-XX:+PrintGCDetails -Xloggc:
Java 9 ve sonrasında şöyle yaparız
class="prettyprint"-Xlog:gc*:file=<gc-log-file-path>
Örnek 
Şöyle yaparız
-verbose:gc -Xloggc:gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps
-XX:+PrintGCTimeStamps -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5
-XX:GCLogFileSize=1M
Şuna benzer bir çıktı alırız. Java 16GB heap ile başlatılıyor. GC'den önce 10.7GB kullanılıyor, GC'den sonra 2790 M'e düşüyor.
[Eden: 8176.0M(8176.0M)->0.0B(8176.0M) Survivors: 16.0M->16.0M 
Heap: 10.7G(16.0G)->2790.4M(16.0G)]
Örnek - PrintGCDateStamps
Dosyaya 2MM'lik 5 dosyaya yazdırmak için şöyle yaparız
-XX:+DisableExplicitGC 
-XX:+PrintGCDetails 
-XX:+PrintGCApplicationStoppedTime 
-XX:+PrintGCApplicationConcurrentTime 
-XX:+PrintGCDateStamps 
-Xloggc:gclog.log 
-XX:+UseGCLogFileRotation 
-XX:NumberOfGCLogFiles=5 
-XX:GCLogFileSize=2000k 
-XX:+PrintGCTimeStamps seçeneği
Prints date and uptime
Şöyle yaparız.
-XX:+PrintGCTimeStamps -XX:+PrintGCDetails
-XX:+PrintAdaptiveSizePolicy -XX:+PrintTenuringDistribution
Prints date and uptime
-XX:+PrintAdaptiveSizePolicy seçeneği
Prints ergonomic decisions
-XX:+PrintTenuringDistribution seçeneği
Print aging information of survivor regions
-XX:+UseGCLogFileRotation
Bu seçenek şöyle kullanılıyor ancak log rotasyonu pek önerilmiyor.
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/home/GCEASY/gc.log 
-XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=20M"

10 Ocak 2019 Perşembe

G1 Garbage Collector Seçenekleri

Giriş
G1 için tüm seçenekler burada.

Örnek
Şöyle yaparız
# These flags help trigger garbage collection before memory balloons, 
# reducing pressure on allocation-heavy services.
-XX:InitiatingHeapOccupancyPercent=30
# This scavenges unused regions during idle CPU cycles, 
# shaving off silent memory bloat without impacting performance.
-XX:G1PeriodicGCInterval=5000

-XX:MaxGCPauseMillis=n seçeneği
"Stop the World" evresinde en fazla ne kadar duraklama istediğimizi belirtiriz. Varsayılan değer 200
Sets a target for the maximum GC pause time. This is a soft goal, and the JVM will make its best effort to achieve it.
-XX:InitiatingHeapOccupancyPercent=n seçeneği
Heap'in yüzde kaçı dolu ise Concurrent GC döngüsü başlatılacağını belirtir.
Percentage of the (entire) heap occupancy to start a concurrent GC cycle. It is used by GCs that trigger a concurrent GC cycle based on the occupancy of the entire heap, not just one of the generations (e.g., G1). A value of 0 denotes 'do constant GC cycles'. The default value is 45.
Açıklaması şöyle.
This flag is set to 45 by default. This means that a GC cycle is triggered when the heap becomes 45 percent filled. Reducing this value means GC would get triggered earlier and more often. But care should be taken that the value is not set to a number that's too low which would result in GCs happening too frequently.
-XX:NewRatio=n seçeneği
Bu seçenek aslında JVM sizing options başlığı altında düşer.
Ratio of new/old generation sizes. The default value is 2.
-XX:SurvivorRatio=n seçeneği
Bu seçenek aslında JVM sizing options başlığı altında düşer.
Ratio of eden/survivor space size. The default value is 8.
-XX:MaxTenuringThreshold=n seçeneği
Maximum value for tenuring threshold. The default value is 15.
-XX:ParallelGCThreads=n seçeneği
Sets the number of threads used during parallel phases of the garbage collectors. The default value varies with the platform on which the JVM is running.
Bu thread'ler "Stop the World" evresinde koşarlar. Açıklaması şöyle.
G1 has both concurrent (runs along with application threads, e.g., refinement, marking, cleanup) and parallel (multi-threaded, e.g., stop the world) phases. Full garbage collections are still single threaded, but if tuned properly your applications should avoid full GCs.
-XX:ConcGCThreads=n seçeneği
Kaç tane thread kullanılacağını belirtir. Concurrent thread'ler uygulama thread'leri ile beraber koşar.
Number of threads concurrent garbage collectors will use. The default value varies with the platform on which the JVM is running.
Açıklaması şöyle.
The default value for this flag is set to the value of ParallelGCThreads plus 2, divided by 4. As long as you have sufficient CPU available on the machine, you can increase this value without incurring any performance penalties.
-XX:G1ReservePercent=n seçeneği
Sets the amount of heap that is reserved as a false ceiling to reduce the possibility of promotion failure. The default value is 10.
Elimizde çok fazla humongous object varsa elle bu değeri değiştirmek gerekebilir.

-XX:G1HeapRegionSize=n 
seçeneği
Normalde bu seçeneği kullanmaya gerek yok. JVM otomatik hesaplıyor. Ancak elimizde çok fazla humongous object varsa elle bu değeri değiştirmek gerekebilir.
Örnek
Bu değeri değiştirmek için şöyle yaparız
XX:G1HeapRegionSize=4M