001: WORA, JIT Compiler và -XX:+PrintCompilation

Bài viết nằm trong series Java memory management & performance. Với bài viết đầu tiên, cùng nhìn sơ lược về cách JVM thực thi các đoạn code của chúng ta như thế nào nhé. 1) WORA Java nổi tiếng với WORA, nghĩa là Write once, run anywhere, viết một lần và chạy mọi nơi. Oh

Bài viết nằm trong series Java memory management & performance.

Với bài viết đầu tiên, cùng nhìn sơ lược về cách JVM thực thi các đoạn code của chúng ta như thế nào nhé.

1) WORA

Java nổi tiếng với WORA, nghĩa là Write once, run anywhere, viết một lần và chạy mọi nơi. Oh shit, wtf… làm sao lại có một ngôn ngữ thần thánh như vậy, bí ẩn phía sau nó là gì?

Lấy ví dụ thực tế, nếu mình chỉ biết tiếng Việt, liệu mình có thể đi du lịch nước ngoài được không? Làm sao mình giao tiếp được với người bản địa. Chẳng có nhẽ với mỗi một quốc gia khác nhau mình phải học ngôn ngữ của họ.

Có 3 cách cơ bản để giải quyết vấn đề trên:

  • Thứ nhất, tham dự một khóa học ngoại ngữ cấp tốc để giao tiếp cơ bản được với người bản địa. Khá tốn, không khả thi.
  • Thứ hai, cũng đi học ngoại ngữ nhưng là international language. Không khác cách đầu tiên mấy.
  • Thứ ba, thuê luôn tour guide cho nhàn, giàu nữa thì chơi hẳn interpreter cho máu. Có vẻ khả thi nhất rồi. Vẫn hơi tốn kém, thôi dùng cái máy thông dịch (khá phổ biến hiện nay) tầm 5 – 8 củ là ngon nghẻ rồi.

Như vậy, bài toán được giải quyết đơn giản với cách thứ ba, đầu tư một lần, dùng nhiều lần. Không cần biết tất cả các loại ngôn ngữ, chỉ cần biết một và.. đi đâu cũng dùng được với chiếc máy interpreter nhỏ gọn.

Java dựa trên idea này để thực hiện WORA, viết một lần dùng mọi nơi. Interpreter lúc này là JVMcompiler. Các ngôn ngữ của từng quốc gia là các hệ điều hành như Windows, MacOS, Linux… và ngôn ngữ chúng ta nói là các đoạn code Java.

Quá trình để một đoạn code Java được thực thi với WORA diễn ra như sau:

  • Mua một cái máy interpreter trước, hay nói cách khác là cài đặt JDK (Java development kit). Nếu chưa rõ các bạn có thể tìm đọc thêm về JDK, JRE và JVM nhé.
  • Sau đó, nói vào máy thông dịch để nó ghi nhận thông tin. Tương ứng với việc viết code trong file .java.
  • Lúc này máy thực hiện chuyển ngôn ngữ của chúng ta sang dạng ngôn ngữ máy có thể hiểu được. Cụ thể đó là chuyển thông tin từ dạng âm thanh sang dạng tín hiệu điện từ để tiến hành phân tích. Với Java, ta phải làm việc này thủ công, đó là thực hiện biên dịch các file .java sang file .class chứa các bytecode là ngôn ngữ của JVM để được thực thi.
  • Cuối cùng, interpreter dịch sang ngôn ngữ bản địa và phát âm thanh. Tương ứng với JVM dịch bytecode sang machine code phù hợp với từng OS khác nhau để thực thi.

Trong thực tế, ứng dụng bao gồm rất nhiều file, sau khi compile ta sẽ nén nó thành một file duy nhất cho gọn, thường là .jar hoặc .war. Đem file jar này đi chạy trên OS nào cũng được, với điều kiện đã có JRE. Write once, run anywhere là như vậy.

2) JIT Compiler

Bỏ qua phần compile code, để start một Java application với jar file, ta sử dụng câu lệnh:

$ java -jar file-name.jar

Sau khi chạy, một JVM mới được tạo ra và thực thi trên đó. Như vậy, mỗi một application chạy trên một JVM riêng biệt, nếu có được hỏi phỏng vấn thì cứ mạnh dạn trả lời nhé 😂.

JVM có nhiệm vụ thông dịch các đoạn code sang mã máy để OS thực thi. Như vậy, có thể nói Javainterpreter language. Nếu bàn về tốc độ thực thi thì không có cửa so với các ngôn ngữ compiled language như C, C++ hay C#

Các ngôn ngữ như C hay C# được trực tiếp biên dịch (compile) ra mã máy nên khi thực thi sẽ nhanh hơn do không tốn công chuyển đổi (interpret) từ bytecode sang native code như Java. Do đó, với compiled language, mỗi khi chạy trên OS khác nhau cần compile lại code để phù hợp với OS đó, không có WORA như Java, bù lại tốc độ thực thi nhanh hơn.

Tuy nhiên nó chỉ là bề nổi của tảng băng chìm, các kĩ sư của Sun/Oracle đã thiết kế JVM với hàng loạt tính năng và thuật toán phức tạp để làm chương trình hoạt động hiệu quả hơn là việc đơn thuần sử dụng interpreter.

JVM sẽ monitor xem đoạn code nào được thực thi nhiều lần. Đoạn code đó có thể là method, một phần của method hoặc một vòng lặp (loop). Sau khi tìm được block of code đó, thay vì mỗi lần thực thi phải interpret sang machine code thì nó được compile sang machine code luôn cho nhanh. Tất nhiên sau khi compile phải có nơi lưu trữ lại native code đó, là code cache. Như vậy, tại một thời điểm, một vài phần của chương trình sẽ không chạy trong trạng thái interpretive nữa mà là thực thi luôn native code.

Việc interpret từ bytecode sang native code hoặc quyết định compile và lưu trữ lại native code đó hay không là nhiệm vụ của Just-in-time Compiler, ngắn gọn hơn là JIT Compiler.

Machine code hay nói cách khác là native code là những đoạn code mà OS có thể hiểu ngay được để thực thi, không cần thông qua interpreter. JIT Compiler là trái tim của JVM, có nhiệm vụ thông dịch bytecode sang native code và giúp cho Java application chạy nhanh hơn. Ngoài ra, nó cũng là lý do vì sao khi benchmark những đoạn code chạy lần đầu luôn chậm hơn các lần sau đó.

Thực ra không cần đi quá sâu vào phần này nếu bạn không cần optimize application. Nên nếu muốn optimize application ta cần hiểu rõ hơn về nó. Let’s continue 😂.

Những đoạn code được chạy thường xuyên (hot code) sẽ được JIT Compilercompile thành native code để tăng tốc độ thực thi. Tương tự, method nào cả ngày chỉ chạy có một vài lần thì.. thôi mỗi lần chạy là một lần interpret cũng được, để dành memory lưu trữ cho các đoạn code khác cần thiết hơn.

Việc compile từ Java bytecode sang native code được thực hiện trên thread độc lập với quá trình interpreter. Bản chất JVM cũng là một application, và là multi-thread application. Một thread thực hiện interpretbytecode sang native code, thread khác thực thi native code đó, và một thread compile bytecode sang native code lưu trữ trên code cache. Do vậy, việc compile sang native code không có bất kì ảnh hưởng gì đến quá trình run application. Nếu chưa có sẵn native code, JVM vẫn sử dụng interpreter để thực thi code. Và khi native code đã sẵn sàng, JVM sẽ chuyển sang dùng luôn, chả tội gì phải interpret nữa.

3) Practice

Đến giờ thực hành rồi, cùng tìm hiểu xem dòng code nào được compile sang native code. Bài toán đơn giản như sau, in ra 10 số nguyên tố đầu tiên. Tạo file Application.java và code thôi:

importjava.util.ArrayList;importjava.util.List;classPrimeNumberGenerator{privatebooleanisPrime(int n){finaldouble result =Math.sqrt(n);for(int i =2; i <= result; i++){if(n % i ==0){returnfalse;}}returntrue;}privateintgetNextPrimeAbove(int previous){int number = previous +1;while(!isPrime(number)){++number;}return number;}publicvoidgenerateNumbers(int count){finalList<Integer> primes =newArrayList<>();
        primes.add(2);int next =2;while(primes.size()<= count){
            next =getNextPrimeAbove(next);
            primes.add(next);}System.out.println(primes);}}publicclassApplication{publicstaticvoidmain(String[] args){finalPrimeNumberGenerator generator =newPrimeNumberGenerator();
        generator.generateNumbers(10);}}

Với Java 8, ta cần thực hiện compile sang bytecode trước khi chạy ứng dụng, tức là cần 2 câu lệnh. Tuy nhiên với Java 11, chỉ cần 1 câu lệnh, nó sẽ thực hiện compile on the fly luôn (chỉ thực hiện được với chương trình có 1 class duy nhất).

Hiện tại mình đang sử dụng Java 11 nhé.

$ java -version

java version "11.0.11"2021-04-20 LTS
Java(TM) SE Runtime Environment 18.9(build 11.0.11+9-LTS-194)
Java HotSpot(TM)64-Bit Server VM 18.9(build 11.0.11+9-LTS-194, mixed mode)

Nguy hiểm tí thôi, mình thực hiện luôn trên IDE cho nhanh. Hoặc dùng command line như bên dưới cho nguy hiểm.

$ javac Application.java
$ java Application

[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31]

Thêm JVM options khi chạy -XX:+PrintCompilation, nó sẽ in thêm các thông tin về quá trình code compilation. Nói qua về JVM options trước, nó là các tham số.. nâng cao trong quá trình thực thi code. Phần lớn JVM options tuân theo format chia làm 3 phần:

  • Phần đầu tiên -XX:
  • Phần tiếp theo là dấu + hoặc nhằm mục đích bật/tắt các options.
  • Phần cuối là tên của options dưới dạng CamelCase, viết hoa các chữ cái đầu tiên của mỗi từ.

JVM options sẽ được thêm ngay sau keywork java khi thực thi chương trình, ví dụ:

$ java -XX:+PrintCompilation Application

Sau khi thêm -XX:+PrintCompilation, kết quả là:

2713       java.lang.StringLatin1::hashCode (42 bytes)2823       java.lang.Object::<init>(1 bytes)2833       java.lang.String::hashCode (49 bytes)2853       java.lang.String::isLatin1 (19 bytes)2843       java.lang.String::coder (15 bytes)2863       java.util.ImmutableCollections$SetN::probe (56 bytes)2873       java.lang.Math::floorMod (10 bytes)2983       java.lang.Math::floorDiv (22 bytes)29103       java.lang.StringLatin1::equals (36 bytes)29113       java.util.ImmutableCollections$SetN::hashCode (46 bytes)2993       java.lang.String::equals (65 bytes)30123       java.util.ImmutableCollections::emptySet (4 bytes)30133       java.util.Set::of (4 bytes)30154       java.lang.StringLatin1::hashCode (42 bytes)30143       java.util.Objects::equals (23 bytes)30163       java.lang.module.ModuleDescriptor$Exports::hashCode (38 bytes)31193       java.util.Objects::requireNonNull (14 bytes)31173       java.util.AbstractCollection::<init>(5 bytes)31183       java.util.ImmutableCollections$AbstractImmutableCollection::<init>(5 bytes)31203       java.util.ImmutableCollections$AbstractImmutableSet::<init>(5 bytes)31223       java.util.Set::of (66 bytes)32231       java.lang.module.ModuleDescriptor::name (5 bytes)32241       java.lang.module.ModuleReference::descriptor (5 bytes)3213       java.lang.StringLatin1::hashCode (42 bytes)   made not entrant
     33214       java.lang.Object::<init>(1 bytes)3323       java.lang.Object::<init>(1 bytes)   made not entrant
     36253       java.lang.String::charAt (25 bytes)36263       java.lang.StringLatin1::charAt (28 bytes)37273       java.util.concurrent.ConcurrentHashMap::tabAt (22 bytes)37283       jdk.internal.misc.Unsafe::getObjectAcquire (7 bytes)3730     n 0       jdk.internal.misc.Unsafe::getObjectVolatile (native)38293       java.util.ImmutableCollections$SetN$SetNIterator::hasNext (13 bytes)38313       java.util.concurrent.ConcurrentHashMap::spread (10 bytes)38333       java.util.ImmutableCollections$SetN$SetNIterator::next (47 bytes)38323       java.util.ImmutableCollections$SetN$SetNIterator::nextIndex (56 bytes)38341       java.util.KeyValueHolder::getKey (5 bytes)39351       java.util.KeyValueHolder::getValue (5 bytes)39363       java.util.ImmutableCollections$MapN::probe (60 bytes)39383       java.util.ImmutableCollections$MapN::get (35 bytes)39393       java.util.HashMap::hash (20 bytes)39373       java.util.KeyValueHolder::<init>(21 bytes)40413       java.util.concurrent.ConcurrentHashMap::addCount (289 bytes)4042     n 0       jdk.internal.misc.Unsafe::compareAndSetLong (native)4045     n 0       jdk.internal.misc.Unsafe::compareAndSetObject (native)4043!3       java.util.concurrent.ConcurrentHashMap::putVal (432 bytes)42443       java.util.concurrent.ConcurrentHashMap$Node::<init>(20 bytes)42483       java.util.concurrent.ConcurrentHashMap::putIfAbsent (8 bytes)42463       java.lang.String::length (11 bytes)4250     n 0       java.lang.System::arraycopy (native)(static)42473       java.util.concurrent.ConcurrentHashMap::casTabAt (21 bytes)43401       java.lang.module.ResolvedModule::reference (5 bytes)43491       java.util.ImmutableCollections$SetN::size (5 bytes)43523       java.util.HashMap::getNode (148 bytes)44543       java.util.HashMap::putVal (300 bytes)4459     n 0       java.lang.Object::hashCode (native)45533       java.util.HashMap::put (13 bytes)45553       java.util.HashMap$Node::<init>(26 bytes)45563       java.util.HashMap::newNode (13 bytes)45573       java.util.HashMap::afterNodeInsertion (1 bytes)46601       java.lang.module.ModuleDescriptor$Exports::source (5 bytes)46513       java.util.HashMap::get (23 bytes)46613       java.util.ImmutableCollections$Set12$1::hasNext (13 bytes)46643       java.util.ImmutableCollections$Set12$1::next (92 bytes)46581       java.lang.module.ModuleDescriptor::isAutomatic (5 bytes)46621       java.lang.module.ResolvedModule::configuration (5 bytes)46631       java.lang.module.ModuleDescriptor$Exports::targets (5 bytes)47653       java.util.Map::entry (10 bytes)47663       java.util.HashSet::add (20 bytes)47671       java.lang.module.ModuleDescriptor::isOpen (5 bytes)48684       java.lang.String::hashCode (49 bytes)48693       java.util.HashMap::resize (356 bytes)4876     n 0       java.lang.Module::addExportsToAllUnnamed0 (native)(static)49713       jdk.internal.module.ModuleBootstrap$2::hasNext (30 bytes)49703       java.util.ImmutableCollections$Set12::size (13 bytes)49743       jdk.internal.module.ModuleBootstrap$2::next (52 bytes)49753       java.util.HashMap::putIfAbsent (13 bytes)49771       java.lang.Module::getDescriptor (5 bytes)50783       java.lang.CharacterDataLatin1::getProperties (11 bytes)5133       java.lang.String::hashCode (49 bytes)   made not entrant
     51734       java.util.ImmutableCollections$SetN$SetNIterator::hasNext (13 bytes)51293       java.util.ImmutableCollections$SetN$SetNIterator::hasNext (13 bytes)   made not entrant
     51724       java.util.HashMap::afterNodeInsertion (1 bytes)51573       java.util.HashMap::afterNodeInsertion (1 bytes)   made not entrant
     52793       java.lang.StringLatin1::indexOf (61 bytes)61684       java.lang.String::hashCode (49 bytes)   made not entrant
     62803       java.lang.AbstractStringBuilder::ensureCapacityInternal (39 bytes)[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31]

Vãi nhái thật 😂, thế này ai chơi. Hít mấy hơi đọc tiếp xem quá trình compilation diễn ra thế nào.

Nhìn sơ qua cách tổ chức thông tin compilation chia làm các column:

  • Với column đầu tiên là các con số tăng dần 27, 28, 29… Nó là số đo thời gian biểu diễn dưới đơn vị miliseconds kể từ khi JVM bắt đầu chạy, chỉ có tăng chứ không giảm.
  • Column tiếp theo là trình tự compile các method hoặc block of code. Nhưng tại sao chúng không sắp xếp theo thứ tự, đơn giản là do có những đoạn code gọi các method được định nghĩa ở các vị trí khác nhau, file khác nhau. Ngoài ra còn do compilation time. Ta chưa cần quan tâm đến column này lắm.
  • Column tiếp theo đa số là các empty value, tuy nhiên có 2 giá trị ở đây là %n: s là synchronized, nnative code, %On Stack Replacement compilation. Tất cả có 5 giá trị chứ không chỉ có 2, discuss về nó sau nhé.
  • Tiếp theo là column có đoạn giá trị từ 0 đến 4, nói về loại compilation. 0 là không cần compile, các giá trị 1 đến 4 là code compilation level theo chiều sâu. Nhìn lại dòng có cột thứ ba giá trị n, vì là native code nên không cần compile nữa, do đó nhận giá trị 0. Level càng cao thì quá trình compile càng nhiều bước, time càng lớn.
  • Column tiếp theo là method name.
  • Theo sau là size của đoạn bytecode đó.

Bạn có nhận ra điều gì kì lạ không, gợi ý ở column cuối cùng.

… waiting …

Đó là.. không thấy code của mình viết ở đâu nhỉ, không có bất kì một dòng nào ngoài System.out.println() cuối cùng 😂.

Chạy lại với và in ra nhiều số nguyên tố hơn, 5000 cho máu, bỏ luôn System.out.println() cho đỡ vướng mắt.

2713       java.lang.StringLatin1::hashCode (42 bytes)2723       java.lang.Object::<init>(1 bytes)2733       java.lang.String::hashCode (49 bytes)2853       java.lang.String::isLatin1 (19 bytes)2843       java.lang.String::coder (15 bytes)2863       java.util.ImmutableCollections$SetN::probe (56 bytes)2873       java.lang.Math::floorMod (10 bytes)2883       java.lang.Math::floorDiv (22 bytes)29103       java.lang.StringLatin1::equals (36 bytes)29113       java.util.ImmutableCollections$SetN::hashCode (46 bytes)2993       java.lang.String::equals (65 bytes)29123       java.util.ImmutableCollections::emptySet (4 bytes)29154       java.lang.StringLatin1::hashCode (42 bytes)30133       java.util.Set::of (4 bytes)30143       java.util.Objects::requireNonNull (14 bytes)30163       java.lang.module.ModuleDescriptor$Exports::hashCode (38 bytes)30173       java.util.AbstractCollection::<init>(5 bytes)30183       java.util.ImmutableCollections$AbstractImmutableSet::<init>(5 bytes)30203       java.util.Set::of (66 bytes)31211       java.lang.module.ModuleDescriptor::name (5 bytes)31221       java.lang.module.ModuleReference::descriptor (5 bytes)3213       java.lang.StringLatin1::hashCode (42 bytes)   made not entrant
     32194       java.lang.Object::<init>(1 bytes)3323       java.lang.Object::<init>(1 bytes)   made not entrant
     34233       java.lang.String::charAt (25 bytes)35243       java.lang.StringLatin1::charAt (28 bytes)36253       java.util.concurrent.ConcurrentHashMap::tabAt (22 bytes)3628     n 0       jdk.internal.misc.Unsafe::getObjectVolatile (native)36263       jdk.internal.misc.Unsafe::getObjectAcquire (7 bytes)36293       java.util.ImmutableCollections$SetN$SetNIterator::nextIndex (56 bytes)37273       java.util.ImmutableCollections$SetN$SetNIterator::hasNext (13 bytes)37301       java.util.KeyValueHolder::getKey (5 bytes)38323       java.util.ImmutableCollections$MapN::probe (60 bytes)38353       java.util.ImmutableCollections$SetN$SetNIterator::next (47 bytes)38311       java.util.KeyValueHolder::getValue (5 bytes)38343       java.util.Objects::equals (23 bytes)38333       java.util.KeyValueHolder::<init>(21 bytes)39363       java.util.concurrent.ConcurrentHashMap::addCount (289 bytes)3939     n 0       jdk.internal.misc.Unsafe::compareAndSetLong (native)3942     n 0       jdk.internal.misc.Unsafe::compareAndSetObject (native)3940!3       java.util.concurrent.ConcurrentHashMap::putVal (432 bytes)41453       java.util.concurrent.ConcurrentHashMap::putIfAbsent (8 bytes)41383       java.util.concurrent.ConcurrentHashMap::spread (10 bytes)41413       java.util.concurrent.ConcurrentHashMap$Node::<init>(20 bytes)41443       java.lang.String::length (11 bytes)41433       java.util.concurrent.ConcurrentHashMap::casTabAt (21 bytes)42463       java.util.HashMap::hash (20 bytes)42371       java.lang.module.ResolvedModule::reference (5 bytes)4248     n 0       java.lang.System::arraycopy (native)(static)42471       java.util.ImmutableCollections$SetN::size (5 bytes)42493       java.util.HashMap::get (23 bytes)43513       java.util.HashMap::getNode (148 bytes)43533       java.util.HashMap::putVal (300 bytes)4359     n 0       java.lang.Object::hashCode (native)44523       java.util.HashMap::put (13 bytes)44543       java.util.HashMap$Node::<init>(26 bytes)44553       java.util.HashMap::newNode (13 bytes)44573       java.util.HashMap::afterNodeInsertion (1 bytes)45563       java.util.ImmutableCollections$Set12$1::hasNext (13 bytes)45601       java.lang.module.ModuleDescriptor$Exports::source (5 bytes)45503       java.lang.module.ResolvedModule::name (11 bytes)45581       java.lang.module.ModuleDescriptor::isAutomatic (5 bytes)45611       java.lang.module.ResolvedModule::configuration (5 bytes)45621       java.lang.module.ModuleDescriptor$Exports::targets (5 bytes)46633       java.util.Map::entry (10 bytes)46643       java.util.HashSet::add (20 bytes)46651       java.lang.module.ModuleDescriptor::isOpen (5 bytes)46664       java.lang.String::hashCode (49 bytes)47673       java.util.ImmutableCollections$Set12::size (13 bytes)47693       jdk.internal.module.ModuleBootstrap$2::hasNext (30 bytes)47703       java.util.HashMap::resize (356 bytes)4775     n 0       java.lang.Module::addExportsToAllUnnamed0 (native)(static)48713       jdk.internal.module.ModuleBootstrap$2::next (52 bytes)48743       java.util.HashMap::putIfAbsent (13 bytes)49763       java.util.ImmutableCollections$MapN::get (35 bytes)49731       java.lang.Module::getDescriptor (5 bytes)4933       java.lang.String::hashCode (49 bytes)   made not entrant
     49684       java.util.ImmutableCollections$SetN$SetNIterator::hasNext (13 bytes)50773       java.lang.CharacterDataLatin1::getProperties (11 bytes)50273       java.util.ImmutableCollections$SetN$SetNIterator::hasNext (13 bytes)   made not entrant
     50724       java.util.HashMap::afterNodeInsertion (1 bytes)50573       java.util.HashMap::afterNodeInsertion (1 bytes)   made not entrant
     51783       java.lang.StringLatin1::indexOf (61 bytes)56664       java.lang.String::hashCode (49 bytes)   made not entrant
     57793       java.lang.AbstractStringBuilder::ensureCapacityInternal (39 bytes)59803       PrimeNumberGenerator::isPrime (34 bytes)59823       java.lang.Number::<init>(5 bytes)59833       java.lang.Integer::<init>(10 bytes)59874       PrimeNumberGenerator::isPrime (34 bytes)59863       PrimeNumberGenerator::getNextPrimeAbove (20 bytes)59843       java.util.ArrayList::add (25 bytes)60811       java.util.ArrayList::size (5 bytes)60853       java.util.ArrayList::add (23 bytes)60803       PrimeNumberGenerator::isPrime (34 bytes)   made not entrant
     60883       java.lang.Integer::valueOf (32 bytes)6089 %     4       PrimeNumberGenerator::isPrime @ 9(34 bytes)61863       PrimeNumberGenerator::getNextPrimeAbove (20 bytes)   made not entrant
     61903       PrimeNumberGenerator::getNextPrimeAbove (20 bytes)62914       PrimeNumberGenerator::getNextPrimeAbove (20 bytes)63903       PrimeNumberGenerator::getNextPrimeAbove (20 bytes)   made not entrant

Đã thấy có chút khác biệt, colum thứ ba có thêm giá trị ! và column cuối đã xuất hiện những dòng code chúng ta viết.

Chú ý vào dòng có giá trị %, method isPrime() được gọi rất nhiều lần và được đặt vào code cache, chính là nơi lưu trữ native code từ quá trình JIT Compiler. Mình sẽ nói cụ thể hơn về nó ở phần sau.

Nếu đọc đến đây thì xin chúc mừng, bạn khá kiên nhẫn đấy 😂. Đón chờ bài tiếp theo về Code cacheCompiler nhé.

Reference

Reference in series https://viblo.asia/s/java-memory-management-performance-vElaB80m5kw

© Dat Bui

Nguồn: viblo.asia

Bài viết liên quan

WebP là gì? Hướng dẫn cách để chuyển hình ảnh jpg, png qua webp

WebP là gì? WebP là một định dạng ảnh hiện đại, được phát triển bởi Google

Điểm khác biệt giữa IPv4 và IPv6 là gì?

IPv4 và IPv6 là hai phiên bản của hệ thống địa chỉ Giao thức Internet (IP). IP l

Check nameservers của tên miền xem website trỏ đúng chưa

Tìm hiểu cách check nameservers của tên miền để xác định tên miền đó đang dùn

Mình đang dùng Google Domains để check tên miền hàng ngày

Từ khi thông báo dịch vụ Google Domains bỏ mác Beta, mình mới để ý và bắt đầ