28 Mayıs 2020 Perşembe

Concurrent Mark Sweep Garbage Collector - Java 8 İçin Belki Kullanılabilir, Java 14 İle Kaldırıldı

Giriş
Yazı uzun, bu yüzden kısaca özetlemek gerekirse
- Young Generation için "Stop the World" emri verilir ve Paralel Mark Copy (Scavenge) kullanır. Yani nesne başka bir bloka taşınır
- Old Generation için eğer mecbur değilse "Stop the World" emri verilmez ve Mark Sweep kullanır. Yani nesne olduğu yerde bırakılır. Aynı bloktaki nesneler arasında boşluklar oluşur. 

Kullanmak
Kullanmak için şöyle yaparız
-XX:+UseConcMarkSweepGC  -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xms1024m       -XX:+PrintCommandLineFlags -jar foo.jar
CMS vs Paralel Collector
CMS ve Paralel Collector bence Young Generation için aynı mantıkta çalışıyor. Her ikisi de "Stop the World" emrini veriyor ve paralel olarak işi hızlıca bitirmeye çalışıyor. Ancak CMS Minor GC işlemini çok daha sık yapıyor, Ayrıca ParNew içinde Old Generation'a bir callback gönderiyor.

Esas fark Old Generation'da ortaya çıkıyor. Paralel Collector, Major GC işleri için de "Stop the World" emri verirken, CMS major GC için concurrent yani uygulama thread'leri ile birlikte çalışarak "Stop the World" emrini mümkün olduğunca az vermeye çalışıyor.

Ayrıca GC biraz daha fazla yük getiriyor. Açıklaması şöyle
Aims for low pause times but has higher overall GC overhead. It’s generally less preferred than G1 in modern Java versions.
Java 9 ve Ötesi
Java 9 ile CMS kullanmaya çalışınca yani şöyle yaparsak
XX:+UseConcMarkSweepGC
bize uyarı veriyor. Uyarı şöyle
Java HotSpot(TM) 64-Bit Server VM warning: Option UseConcMarkSweepGC was deprecated in 
version 9.0 and will likely be removed in a future release.
Bu GC Java 14 ile tamamen kaldırıldı. Açıklaması şöyle.
As of Java 9, the CMS garbage collector has been deprecated. Therefore, JVM prints a warning message if we try to use it:
...
Moreover, Java 14 completely dropped the CMS support:
Yerine Ne Kullanabiliriz ?
- Java 9 için G1 kullanılabilir
- Java 11,12 için Z kullanılabilir

Law Pause Ne Demek
Bu Garbage Collector "Low Pause" algoritmalardan bir tanesi. Stop the World emrini mümkün olduğunca az veriyor. Açıklaması şöyle.
The Concurrent Mark Sweep (CMS) collector is designed to be a lower latency collector than the parallel collectors. The key part of this design is trying to do part of the garbage collection at the same time as the application is running. This means that when the collector needs to pause the application's execution it doesn't need to pause for as long.

CMS uses the parallel stop-the-world mark-copy algorithm in the Young Generation and concurrent mark-sweep algorithm in the Old Generation.
Yani işlemci ve belleği daha fazla kullanarak, daha az Stop the World emrini veriyor. Açıklaması şöyle
It's designed for applications that prefer shorter garbage collection pauses, and that can afford to share processor resources with the garbage collector while the application is running.

Simply put, applications using this type of GC respond slower on average but do not stop responding to perform garbage collection.
Footprint
Açıklaması şöyle. Parallel Collector 'a göre daha fazla bellek harcar ancak karşılığında daha kısa duraklama (Low Pause) sağlar.
This CMS collector requires more footprint than Parallel Collector and it has more data structures to take care of. It has less throughput than the Parallel Collector but the advantage is it has smaller pauses than the Parallel Collector. This collector is the best-used collector for all the general Java applications
CMS Collector Belleği İşletim Sistemine Geri Verir
Açıklaması şöyle. Ancak bir kaç full GC işleminden sonra geri verir.
Serial and ConcMarkSweep are also shrinking garbage collectors and can scale memory usage in JVM vertically. But in comparison to G1, they require 4 Full GC cycles to release all unused resources.
2. CMS Nedir
Açıklaması şöyle
It attempts to minimize the pauses due to garbage collection by doing most of the garbage collection work concurrently with the application threads. It uses the parallel stop-the-world mark-copy algorithm in the Young Generation and the mostly concurrent mark-sweep algorithm in the Old Generation.
3. Young Generation - Mark Copy 
Young Generation çalışırken "Stop the World" emri verilir. Mark-copy olduğu için yaşamasına karar verilen nesneler başka bloğa taşınır

Young Generation için Serial veya Parallel seçenekleri ile çalıştırılabilir.

3.1. Serial Seçeneği - Kullanmayın
Serial için şöyle yaparız. UseParNewGC'nin başında eksi var. Bu yöntem deprecated olarak işaretli ve Java 9 ile de kaldırıldı. Dolayısıyla kullanmamak lazım
-XX:+UseConcMarkSweepGC  -XX:-UseParNewGC
3.2. UseParNewGC Seçeneği  - parallel copying collector
Eğer sadece UseConcMarkSweepGC belirtilirse bu seçenek etkinleştirilmiş varsayılır. Açıklaması şöyle.
UseParNewGC A parallel version of the young generation copying collector is used with the concurrent collector (i.e. if -XX:+ UseConcMarkSweepGC is used on the command line then the flag UseParNewGC is also set to true if it is not otherwise explicitly set on the command line).
Bu seçeneği illaki belirtmek istersek şöyle yaparız.
XX:+UseConcMarkSweepGC -XX:+UseParNewGC
Açıklaması şöyle
The parallel copying collector (Enabled using -XX:+UseParNewGC). Like the original copying collector, this is a stop-the-world collector. However this collector parallelizes the copying collection over multiple threads, which is more efficient than the original single-thread copying collector for multi-CPU machines (though not for single-CPU machines). This algorithm potentially speeds up young generation collection by a factor equal to the number of CPUs available, when compared to the original singly-threaded copying collector.
Bu yüzden GC loglarında şuna benzer bir çıktı görürüz
[GC (Allocation Failure) ...  [ParNew: ....]
Açıklaması şöyle
Parallel New (ParNew) Collector (invoked by "-XX:+UseConcMarkSweepGC" option) - The ParNew collector for Young generation uses the "Copy (also called Scavenge)" algorithm parallelly using multiple CPU processors (multiple threads) in a stop-the-world fashion.

The ParNew collector for Young (new) generation uses the same algorithm as the PS collector, except that it has an internal 'callback' that allows an old generation collector to operate on the objects it collects (really written to work with the concurrent collector), as described by Jack Shirazi in "Oracle JVM Garbage Collectors Available From JDK 1.7.0_04 And After".

The ParNew collector for Young generation can be identified in GC log messages with the "ParNew" label, as in this example: "ParNew: 1068K->63K(1152K)".

In JVM 9 and older releases, the ParNew collector for Young generation can be invoked explicitly using the "-XX:+UseParNewGC" option. But this option has been removed from JVM 10.
4. Old Generation - Concurrent Mark Sweep
Old Generation için uygulama ile eş zamanlı çalışarak "Stop the World" emrinin mümkün olduğunca az olmasına uğraşır. Mark-sweep olduğu için yaşamasına karar verilen nesneler oldukları yerde bırakılırlar. Yani başka bloğa veya yan yana olacak şekilde yer değiştirmez. Aynı bloktaki nesneler arasında boşluklar oluşur.

Bu Garbage Collector Java 9'dan itibaren kullanılmamalı. Açıklaması şöyle.  Java 9'dan itibaren G1 artık en çok tercih edilen garbage collector.
The Concurrent Mark Sweep (CMS) collector has been deprecated since Java 9...

Shenandoah Garbage Collector

Giriş
Açıklaması şöyle.
There is one more collector called Shenandoah Collector. This collector is an improvement upon G1 collector wherein it requires a little higher footprint so it takes more data structures behind the scenes but it has even lower latency than G1 collector.

Shenandoah is an ultra-low pause time garbage collector that reduces GC pause times by performing more garbage collection work concurrently with the running Java program. CMS and G1 both perform concurrent marking of live objects. Shenandoah adds concurrent compaction.

Date Sınıfı - Kullanmayın

Giriş
Şu satırı dahil ederiz
import java.util.Date;
Saat dilimi bilgisinden mahrumdur, sadece epoch'tan - yani January 1, 1970, 00:00:00 GMT veya UTC - beri geçen süreyi, milisaniye olarak saklar.  Açıklaması şöyle.
The class Date represents a specific instant in time, with millisecond precision.
Sakladığı milisaniyeyi gün,ay yıl, saat gibi parçalamak için uygun metodlar sunmaz. Bu iş için Calendar sınıfı daha uygundur.

Maden Epoch'tan Beri Geçen Süreyi Saklıyor, Neden Bunu Göremiyoruz
Açıklaması şöyle. Çünkü bu nesnenin toString() metodunu UTC değeri yerel saate çeviriyor.
When you print an object of java.util.Date, its toString method returns the date-time in the JVM's timezone, calculated from this milliseconds value. If you need to print the date-time in a different timezone, you will need to set the timezone to SimpleDateFormat and obtain the formatted string from it.
constructor - default
Şöyle kurulur.
Date timeNow = new Date();
Bu kod aslında şuna denktir.
Date timeNow = new Date(System.currentTimeMillis());
Sınıf yazdırılabilir.
System.out.println(new java.util.Date());
constructor - milliseconds
Epoch'tan itibaren geçen milisaniye ile nesneyi oluşturur.
Örnek
Şöyle yaparız.
long ms = 1469109609000;
Date date = new Date(ms);
Örnek
Bu constructor calendar.setTimeInMillis() ile aynıdır. Şöyle yaparız.
Long timeInMilis = 1509966414000l;
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(timeInMilis);
System.out.println("Using Calendar instance : "+cal.getTime());

Date date = new Date(timeInMilis);
System.out.println("Using date instance : "+date);
Çıktı olarak şunu alırız.
Using Calendar instance : Mon Nov 06 16:36:54 IST 2017
Using date instance : Mon Nov 06 16:36:54 IST 2017
constructor - yıl + ay + gün  - Kullanmayın
Bu metod yerine GregorianCalendar kullanılabilir.

İmzası şöyle.
new Date(int year, int month, int date) 
Bu metod eski ve  kullanılmamalı açıklaması şöyle.
/**
 * Allocates a <code>Date</code> object and initializes it so that
 * it represents midnight, local time, at the beginning of the day
 * specified by the <code>year</code>, <code>month</code>, and
 * <code>date</code> arguments.
 *
 * @param   year    the year minus 1900.
 * @param   month   the month between 0-11.
 * @param   date    the day of the month between 1-31.
 * @see     java.util.Calendar
 * @deprecated As of JDK version 1.1,
 * replaced by <code>Calendar.set(year + 1900, month, date)</code>
 * or <code>GregorianCalendar(year + 1900, month, date)</code>.
 */
Yani eğer yıl olarak 1994 yılına erişmek istersek ilk parametre 94 olmalı. Bu metod yerine Calender.set(...) metodu kullanılabilir. Ancak fark olarak yıl parametresi 94 yerine 1994 olmalı. Yıl değerinin neden 1900'den başladığına dair bir açıklama şöyle.
Basically the original java.util.Date designers copied a lot from C. What you're seeing is the result of that - see the tm struct. So you should probably ask why that was designed to use the year 1900. I suspect the fundamental answer is "because we weren't very good at API design back when tm was designed." I'd contend that we're still not very good at API design when it comes to dates and times, because there are so many different use cases.
constructor - yıl + ay + gün + saat + dakika - Kullanmayın
Örnek ver

constructor - yıl + ay + gün + saat + dakika + saniye - Kullanmayın
Örnek ver

constructor - String - Kullanmayın
Örnek ver

before metodu
İki Date nesnesini karşılaştırır. Java 8 ile gelen yeni sınıflar isBefore(...) isimli metodlar kullanıyor. Bence daha anlaşılır olmuş.
Date startDate = ...; Date date = ...;
if (date.before(startDate)) {...}
compareTo metodu
Örnek
En büyük tarihi bulmak için şöyle yaparız.
Date finalexamDate = Person.stream()
  .map(PersonDetails::getexamDate)
  .max(Date::compareTo)
  .orElse(null);
from metodu
Java 8 ile gelen Instant nesnesinden Date oluşturur.
LocalDate currentDate = LocalDate.now();
Date d1 = Date.from(currentDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
Aynı şeyi LocalDateTime ile de yapabiliriz.
LocalDateTime localDateTime = ...
Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
getTime
Epoch'tan beri geçen süreyi milisaniye olarak alırız.
Örnek
İki Date arasındaki farkı şöyle alırız.
Date dateA = ...;
Date dateB = ...;

// detect difference
long diffInMillis = Math.abs(dateA.getTime() - dateB.getTime());
if (diffInMillis < 5 * 60 * 1000) {
    // all is good
} else {
    // bad: 5 or more minutes apart
}
toInstant
Eski Java sınıflarını yeni sınıflara çevirmek için kullanılır.
Instant instant = myDate.toInstant();
toLocaleString metodu - Kullanmayın
Bu metodu yerine
DateFormat.getDateTimeInstance()
ile formatlama yapılabilir.

UTS metodu- Kullanmayın
Örnek ver