Memory area in JVM virtual machine

Keywords: Java Eclipse Mini Program

The running of a program in Java can not be separated from jvm's strict division of memory regions, each region is responsible for its own duties, in order to ensure the correct operation of the program.

A flowchart of a program from file to run

The memory area in JVM can be divided into five categories. Program counters, virtual machine stacks, stacks, method areas, local method stacks. The roles of each area are explained in turn below.

Program counter

Role: remember the execution address of the next instruction

Feature: Thread Private

Storage location: Registers (very fast)

Is there a memory overflow: No, it is the only area where no memory overflow will occur (as specified by the Java Virtual Machine Specification)

The above figure is a compiled byte code of the program, which is a JVM instruction. The instruction set of each operating system is the same. This also explains that java compiles once and runs everywhere. These instructions are not understood by the computer. An interpreter is needed in the middle to convert these instructions into machine code. After the interpretation, the CPU can run these machine code instructions.

java code - > byte code - > JVM instruction - > machine code - > CPU reads machine code instruction set, runs

VM Stack

Meaning: Memory space required for threads to run

Features: Threads are private, FIFO

Component: One or more stack frames, each method call means opening a stack frame.

Stack frame: Memory required for each method to run (local variable, parameter, return address)

Default size: Linux / Mac OS is 1M. Windows depends on Win's virtual memory

Parameters for setting stack size: -Xss1m

Each thread invokes a method with a stack, and each thread in the method has a local variable that does not interfere with each other.

There are two cases of OOM on a virtual machine stack, one is too many stack frames (recursive) and the other is too large (too many local variables, more memory is occupied than stack memory)

Eg:

package com._12306.SellTickets;

public class Test4 {

    private static int count;

    public static void main(String[] args) {
        try {
            method1();
        } catch (Throwable e) {
            e.printStackTrace();
            System.out.println(count);
        }
    }

    private static void method1() {
        count++;
        method1();
    }

}

The demo above does not set a stop condition, and constantly calls the method1 method in the main method, only entering and leaving. By the time of 22204 times, the stack can no longer accommodate so many stack frames, and an error of stack memory overflow will be reported.

Native Method Stack

Role: Local method services used by virtual machines (methods that call other languages through interfaces)

Functionality: Similar to a virtual machine stack, it combines the two in a HotSpot virtual machine

Features: Threads are private, OOM appears

heap

Role: Objects created by the new keyword are stored in the heap

Features: Thread sharing, security issues, garbage collection mechanism to clean up unreferenced objects

Zoning: New Generation, Older Generation, Permanent Generation

VM parameter to set heap size: -Xmx2m

Heap memory overflow is caused by the constant creation of objects that are referenced and cannot be recycled by GC. As objects accumulate, heap memory is exhausted and exceptions are thrown.

Eg

package com._12306.SellTickets;

import java.util.ArrayList;
import java.util.List;

public class Test5 {
    public static void main(String[] args) {
        int i = 0; //Counter
        try {
            List<String> arraylist = new ArrayList<>(); //String object is referenced by arrayList, GC cannot recycle
            String a = "Hello";
            while (true) {  //Infinite loop
                arraylist.add(a);  //Continuously add strings to the collection, twice as often as before
                a += a;
                i++;
            }
        } catch (Throwable e) {
            e.printStackTrace();
            System.out.println(i);
        }
    }

}

StringTable

Role: Objects that store strings to improve string utilization.

Bottom implementation: Hash table to ensure that each string is unique.

Convenience: Save memory and read faster (locate string objects quickly based on literal size)

Storage location: 1.6 in permanent generation, 1.7 in heap. (The efficiency of permanent generation recycling is not high.)

Features: Thread sharing, and very secure, once created will not change, there will be OOM

OOM demo, setting VM parameters: -Xmx10m-XX:-UseGCOverheadLimit

Eg:

package com._12306.SellTickets;

import java.util.ArrayList;
import java.util.List;

public class Test14 {

    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        int i = 0;
        try {
            for (int j = 0; j < 260000; j++) {
                list.add(String.valueOf(i).intern()); //Place object i in a StringTable string pool
                i++;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            System.out.println(i);
        }
    }

}

Result

StringTable encountered a GC

Eg

package com._12306.SellTickets;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * Demonstrate StringTable garbage collection
 * -Xmx10m -XX:+PrintStringTableStatistics -XX:+PrintGCDetails -verbose:gc
 */
public class Test15 {
    public static void main(String[] args) throws InterruptedException {
        int i = 0;
        try {
            for (int j = 0; j < 40000; j++) { // j=100, j=40000
                String.valueOf(j).intern();
                i++;
            }
        } catch (Throwable e) {
            e.printStackTrace();
        } finally {
            System.out.println(i);
        }
    }
}

Result

The state of GC after recycling, which originally needed to hold more than 60,000 string objects, now only 3,829, the new generation carried out two GC.

Method Area

Purpose: Store structure-related information about classes, member variables, methods, constructors for classes, running constant pools

Storage location: 1.8 was previously called the permanent generation and stored in the heap. 1.8 was called Metaspace and stored in local memory

Features: Thread sharing, not easy OOM

VM parameter to set method area size: -XX:MaxMetaspaceSize=10m

The memory overflow in the method area is due to too many byte code files that generate classes, of which the large use of cglib dynamic proxies is the main cause.

Since 1.8 uses the operating system's memory, my computer has 4G of memory, and it's not easy to fill it up. The example below sets the method area's memory to 10M.

Eg:

package com._12306.SellTickets;

import jdk.internal.org.objectweb.asm.ClassWriter;
import jdk.internal.org.objectweb.asm.Opcodes;

public class Test7 extends ClassLoader {
    public static void main(String[] args) {
        int j = 0;
        try {
            Test7 test = new Test7();
            for (int i = 0; i < 40000; i++,j++) {
                //ClassWriter's role is to claim the class's binary byte code
                ClassWriter cw = new ClassWriter(0);
                //Version number, public, class name, package name, class, interface
                cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, "Class" + i, null, "java/lang/Object", null);
                //Return byte[]
                byte[] code = cw.toByteArray();
                //Execute class loading
                test.defineClass("Class" + i, code, 0, code.length);
            }
        } finally {
            System.out.println(j);
        }
    }
}

Constant pool (part of method area)

Meaning: is a table from which virtual machine instructions find the class name, method name, parameter type, literal amount to be executed

And so on.

Generation Period: Created when compiled into a byte code file by the compiler

Location: In a byte code file, by command Javap-v byte code file.class Viewable

Hello, World is the first program for beginners, so let's talk about it next

package com._12306.SellTickets;

public class Test8 {
    public static void main(String[] args) {
        System.out.println("Hello,World!");
    }
}

Because Idea is smart, the binary byte code file will be recompiled into a java file, and the byte code file will be decompiled using javap

HackerZhao:SellTickets apple$ javap -v Test8.class 
Classfile /Users/apple/IdeaProjects/aliossdemo/target/classes/com/_12306/SellTickets/Test8.class
  Last modified 2021-9-9; size 569 bytes
  MD5 checksum 4298cd01954f8e99de5ef1340de3ace6
  Compiled from "Test8.java"
public class com._12306.SellTickets.Test8
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #6.#20         // java/lang/Object."<init>":()V
   #2 = Fieldref           #21.#22        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = String             #23            // Hello,World!
   #4 = Methodref          #24.#25        // java/io/PrintStream.println:(Ljava/lang/String;)V
   #5 = Class              #26            // com/_12306/SellTickets/Test8
   #6 = Class              #27            // java/lang/Object
   #7 = Utf8               <init>
   #8 = Utf8               ()V
   #9 = Utf8               Code
  #10 = Utf8               LineNumberTable
  #11 = Utf8               LocalVariableTable
  #12 = Utf8               this
  #13 = Utf8               Lcom/_12306/SellTickets/Test8;
  #14 = Utf8               main
  #15 = Utf8               ([Ljava/lang/String;)V
  #16 = Utf8               args
  #17 = Utf8               [Ljava/lang/String;
  #18 = Utf8               SourceFile
  #19 = Utf8               Test8.java
  #20 = NameAndType        #7:#8          // "<init>":()V
  #21 = Class              #28            // java/lang/System
  #22 = NameAndType        #29:#30        // out:Ljava/io/PrintStream;
  #23 = Utf8               Hello,World!
  #24 = Class              #31            // java/io/PrintStream
  #25 = NameAndType        #32:#33        // println:(Ljava/lang/String;)V
  #26 = Utf8               com/_12306/SellTickets/Test8
  #27 = Utf8               java/lang/Object
  #28 = Utf8               java/lang/System
  #29 = Utf8               out
  #30 = Utf8               Ljava/io/PrintStream;
  #31 = Utf8               java/io/PrintStream
  #32 = Utf8               println
  #33 = Utf8               (Ljava/lang/String;)V
{
  public com._12306.SellTickets.Test8();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 3: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/_12306/SellTickets/Test8;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #3                  // String Hello,World!
         5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 6: 0
        line 7: 8
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       9     0  args   [Ljava/lang/String;
}
SourceFile: "Test8.java"

Constant pool is the constant pool of the process, which contains instructions.

Above is the compiled main method, which starts at 0:getstatic #2, finds the instruction #2 from the constant pool table, followed by #21, #22 and #21, and #22 finds the information you want all the time, knowing that this is an output statement: Field java/lang/System.out:Ljava/io/PrintStream;

Runtime Constant Pool

The constant pool is in the *.class file. When the class is loaded, its constant pool information is put into the run-time constant pool and the symbolic address inside becomes the real address.

Direct memory

Not memory management for a java virtual machine, but system memory

Common in nio operations, do buffer memory when nio data is read and written

Cost of allocation and recycling is high, but read and write performance is good

Not managed by JVM memory recycling

Common performance monitoring, troubleshooting tools

Scenario 1: High CPU usage

package com._12306.SellTickets;

import java.util.concurrent.TimeUnit;

public class Test6 {

    public static void main(String[] args) {
        new Thread(null,() ->{
            while(true){

            }
        },"thread1").start();

        new Thread(null,() ->{
            System.out.println("1...");
            try {
                TimeUnit.SECONDS.sleep(1000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"thread2").start();

        new Thread(null,() ->{
            System.out.println("2...");
            try {
                TimeUnit.SECONDS.sleep(1000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"thread3").start();

    }

}

Run the program, thread1 because of the program while(true) infinite cycle, resulting in high CPU usage.

View the process id, cpu information through the top command, as shown below

View process details through the jstack command

Scenario 2: Deadlock, waiting for each other's lock, no one will let go.

package com._12306.SellTickets;

import java.util.concurrent.TimeUnit;

public class Test9 {
    static A a = new A();
    static B b = new B();

    public static void main(String[] args) throws InterruptedException {

        new Thread(() -> {
            synchronized (a) {  //Thread 1 falls asleep after lock a
                try {
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (b) {    //Try to acquire lock b after waking up, which is owned by thread 2
                    System.out.println("I got the lock a and b");
                }
            }
        }).start();


        new Thread(() -> {
            synchronized (b) {  //Thread 2 gets lock b
                synchronized (a) {    //Thread 2 tries to acquire lock a, which is owned by thread 1
                    System.out.println("I got the lock a and b");
                }
            }
        }).start();
    }
}

class A {
}

class B {
}

Find process id through jsp command

Get the status of the process through jstack and process id

Scenario 3: Heap memory consumption is too large, how is the troubleshooting process?

Eg

package com._12306.SellTickets;

import java.util.concurrent.TimeUnit;

public class Test10 {

    public static void main(String[] args) throws InterruptedException {
        System.out.println("1...");   //Phase 1
        TimeUnit.SECONDS.sleep(30);
        byte[] bytes = new byte[1024 * 1024 * 10]; // 10MB
        System.out.println("2...");   //Phase 2, adding up memory to 10M
        TimeUnit.SECONDS.sleep(20);
        bytes = null;  //Release reference here
        System.gc();  //Garbage collection
        System.out.println("3...");  //Phase 3, post-garbage collection status
        TimeUnit.SECONDS.sleep(100);
    }

}

Open a visualization window using the jconsole command

Scenario 4: After multiple garbage collections, heap memory usage remains high.

Use the jvisualvm command to open the visualization window, unlike jconsole above, where you can dump heap memory information to see which object is consuming a lot of memory.

java source code

package com._12306.SellTickets;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

public class Test11 {

    public static void main(String[] args) throws InterruptedException {
        List<Student> list = new ArrayList<>();
        for (int i = 0; i < 200; i++) {
            list.add(new Student());
        }
        TimeUnit.SECONDS.sleep(200000L);
    }

}

class Student{
    private byte[] bytes = new byte[1024*1024];
}

String in Constant Pool

String s1 = "a",String s2 = "b"

Is String s3 = s1 + s2 equal to String s4 = ab

This question has puzzled many beginners, including bloggers. But everything works for a reason, and once you know the result, you want to know how it works. Now I'll explain its underlying principle from the byte code perspective.

Eg:

package com._12306.SellTickets;


//StringTable is a
public class Test12 {

    public static void main(String[] args) {
        String s1 = "a";
        String s2 = "b";
        String s3 = "ab";
    }
}

Javap-v decompile Test12.class

Classfile /Users/apple/IdeaProjects/aliossdemo/target/classes/com/_12306/SellTickets/Test12.class
  Last modified 2021-9-11; size 516 bytes
  MD5 checksum 58ed70d6cf6fa5fab5e81a28b5e092a2
  Compiled from "Test12.java"
public class com._12306.SellTickets.Test12
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #6.#24         // java/lang/Object."<init>":()V
   #2 = String             #25            // a
   #3 = String             #26            // b
   #4 = String             #27            // ab
   #5 = Class              #28            // com/_12306/SellTickets/Test12
   #6 = Class              #29            // java/lang/Object
   #7 = Utf8               <init>
   #8 = Utf8               ()V
   #9 = Utf8               Code
  #10 = Utf8               LineNumberTable
  #11 = Utf8               LocalVariableTable
  #12 = Utf8               this
  #13 = Utf8               Lcom/_12306/SellTickets/Test12;
  #14 = Utf8               main
  #15 = Utf8               ([Ljava/lang/String;)V
  #16 = Utf8               args
  #17 = Utf8               [Ljava/lang/String;
  #18 = Utf8               s1
  #19 = Utf8               Ljava/lang/String;
  #20 = Utf8               s2
  #21 = Utf8               s3
  #22 = Utf8               SourceFile
  #23 = Utf8               Test12.java
  #24 = NameAndType        #7:#8          // "<init>":()V
  #25 = Utf8               a
  #26 = Utf8               b
  #27 = Utf8               ab
  #28 = Utf8               com/_12306/SellTickets/Test12
  #29 = Utf8               java/lang/Object
{
  public com._12306.SellTickets.Test12();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 3: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/_12306/SellTickets/Test12;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=4, args_size=1
         0: ldc           #2                  // String a
         2: astore_1
         3: ldc           #3                  // String b
         5: astore_2
         6: ldc           #4                  // String ab
         8: astore_3
         9: return
      LineNumberTable:
        line 6: 0
        line 7: 3
        line 8: 6
        line 9: 9
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      10     0  args   [Ljava/lang/String;
            3       7     1    s1   Ljava/lang/String;
            6       4     2    s2   Ljava/lang/String;
            9       1     3    s3   Ljava/lang/String;
}
SourceFile: "Test12.java"

Execute String s1 = "a" in three steps

  1. Get this literal from the pool of run constants by instruction
  2. Getting this literal amount object from a StringTable will not encapsulate it into an object and put it into a StringTable
  3. Place a reference to variable s1 in the member variable table

Stringtable (string pool) jdk1.8 is stored in the heap (shown in the method area), the bottom is a hash table, and the size is fixed and cannot be expanded.

Is Eg1:String s3 = s1 + s2 equal to String s4 = ab

package com._12306.SellTickets;

public class Test12 {

    public static void main(String[] args) {
        String s1 = "a";
        String s2 = "b";
        String s3 = "ab";
        /**
         *         9: new           #5       // class java/lang/StringBuilder   Create a new StringBuilder object
         *         12: dup
         *         13: invokespecial #6      // Method java/lang/StringBuilder."<init>":()V    Initialize StringBuilder
         *         16: aload_1               //                                    Loading local variable variable variable s1 (get reference address)
         *         17: invokevirtual #7      // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;   Do stitching sb.append("a")
         *         20: aload_2               //                                    Load local variable variable s2 (get reference address)
         *         21: invokevirtual #7      // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;   Do stitching sb.append("b")
         *
         * 
         *         24: invokevirtual #8      // Method java/lang/StringBuilder.toString:()Ljava/lang/String;    Call StringBuilder's toString() method to create a new String object in the heap
         *         27: astore        4       //                                    Write back variable s4 to local variable table
         */
        String s4 = s1 + s2;        //s4 is an object in the heap and s3 is an object in a StringTable
        System.out.println(s3 == s4);   //false
    }
}

Unequal

String s3 = s1 + s2 did two variable splicing operations with StringBuilder, and created an "ab" object in heap memory.

String s4 = "ab" creates an object in the constant pool

Address references differ between the two

Eg2:String s3 = "a b" is equal to String s4 = "a"+"b"

Equally, when the java compiler compiles, it knows that these two constants are spliced by default, and then goes to the constant pool to find objects.

Simpler, don't show it here

Eg3: Use the Intern() method to put a String in the heap into a StringTable

package com._12306.SellTickets;

public class Test13 {

    public static void main(String[] args) {

        /**
         *          0: new           #2                  // class java/lang/StringBuilder           Create StringBuilder Object
         *          3: dup
         *          4: invokespecial #3                  // Method java/lang/StringBuilder."<init>":()V     Initialize StringBuilder
         *          7: new           #4                  // class java/lang/String                  Create String Object
         *         10: dup
         *         11: ldc           #5                  // String a                                Load literal a in constant pool, if not created in string pool
         *         13: invokespecial #6                  // Method java/lang/String."<init>":(Ljava/lang/String;)V          Initialize String
         *         16: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;   Object a in Split String Pool
         *         19: ldc           #8                  // String b                                Load literal b in constant pool, if not created in string pool
         *         21: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;   Split object b in string pool
         *         24: invokevirtual #9                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;        Converts a StringBuilder object to a String (created in the heap)
         *         27: astore_1                                                                          Loading variable s into member variable table
         */
        String s = new String("a") + "b";

        /**
         *         28: aload_1                      Loading variables s from the member variable table
         *         29: invokevirtual #10                 // Method java/lang/String.intern:()Ljava/lang/String;   Call the intern() method in String
         *         32: astore_2                     Loading variable s2 into member variable table
         */
        String s2 = s.intern();  //Attempts to put this string object into the string pool, but does not if there is one. If not, returns the object in the string pool

        System.out.println(s2 == s|| s == "ab");     //The string object AB is now placed in the constant pool, and what is taken from the constant pool is also put in (that is, it is not necessary to take the "ab" string from the heap according to the address reference, but from the constant pool when you take it again)

    }

}

The variable s in the example originally refers to the string object "ab" in heap memory and is now placed in the constant pool, where it has been transformed into an element in the constant pool when retrieved again.

Eg4:String s = new String("ab")+"c" created several String objects

Three, ab, c in StringTable, abc in heap;

Some people will say four, and the fourth should refer to the "abc" in StringTable.

I'll prove why there are three

package com._12306.SellTickets;

public class Test12 {

    public static void main(String[] args) {

        /**
         *          0: new           #2                  // class java/lang/StringBuilder           Create StringBuilder object in heap
         *          3: dup
         *          4: invokespecial #3                  // Method java/lang/StringBuilder."<init>":()V     Call StringBuilder initialization method
         *          7: new           #4                  // class java/lang/String                  Create String Object
         *         10: dup
         *         11: ldc           #5                  // String ab                               Load the string object ab in the constant pool by instructions, or create the ab in the first constant pool if not
         *         13: invokespecial #6                  // Method java/lang/String."<init>":(Ljava/lang/String;)V     Initialize String Object
         *         16: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;  Call sb's append() method
         *         19: ldc           #8                  // String c                                Load the string object c in the constant pool, or create it if not, c in the second constant pool
         *         21: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;  Do stitching operation, abc after stitching
         *         24: invokevirtual #9                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;   StringBuilder To String, a new String object abc is created in the heap.
         *         27: astore_1
         *         28: return
         */
        String s = new String("ab") + "c";  // s = new String("abc");

        String intern = s.intern();     //If you don't put it in, it means that there was an object "abc" in the string pool, and s == "abc" is false, then I'm wrong. If you put it in, it means that there were no objects "abc" in the string pool before, that is, three objects were generated.

        System.out.println(s == "abc");
    }
}

performance tuning

Scenario 1: Read all the data from a varchar field in the table from the database and return to the page.

package com._12306.SellTickets;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

/**
 * -Xmx500m -XX:+PrintStringTableStatistics -XX:StringTableSize=200000
 */
public class Test17 {

    public static void main(String[] args) throws IOException {

        List<String> address = new ArrayList<>();  //Simulate database read string
        System.in.read();   //Click on the space to start reading
        for (int i = 0; i < 10; i++) {  //Read 10 times
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream("linux.words"), "utf-8"))) {
                String line = null;
                long start = System.nanoTime();     //Record start time
                while (true) {
                    line = reader.readLine();
                    if (line == null) {
                        break;
                    }
                    address.add(line);      //Add the words you read to the list collection (10 iterations, 9 iterations of data are duplicated, creating String objects in the heap and consuming memory)
                }
                System.out.println("cost:" + (System.nanoTime() - start) / 1000000);    //computing time
            }
        }
        System.in.read();


    }
}

Press the space and start reading the file because the object has references and cannot be reclaimed by GC. Memory will soon be full

Modify the code to add string objects to the string pool

package cn.itcast.jvm.t1.stringtable;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

/**
 * Demonstrate intern to reduce memory usage
 * -XX:StringTableSize=200000 -XX:+PrintStringTableStatistics
 * -Xsx500m -Xmx500m -XX:+PrintStringTableStatistics -XX:StringTableSize=200000
 */
public class Demo1_25 {

    public static void main(String[] args) throws IOException {

        List<String> address = new ArrayList<>();
        System.in.read();
        for (int i = 0; i < 10; i++) {
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream("linux.words"), "utf-8"))) {
                String line = null;
                long start = System.nanoTime();
                while (true) {
                    line = reader.readLine();
                    if(line == null) {
                        break;
                    }
                    address.add(line.intern());  //Attempting to add a string to a StringTable
                }
                System.out.println("cost:" +(System.nanoTime()-start)/1000000);
            }
        }
        System.in.read();


    }
}

50% memory usage

String objects in a string pool cannot be duplicated. The rest of the objects that are not joined to the string pool will be recycled by the GC.

Scenario 2: The impact of string pool size on Performance

Set the number of hash buckets for the string pool with VM parameter: -XX:StringTableSize=1009 (minimum 1009)

Eg

package com._12306.SellTickets;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;

/**
 * Demonstrate the impact of string pool size on Performance
 * -Xms500m -Xmx500m -XX:+PrintStringTableStatistics -XX:StringTableSize=1009
 */
public class Test16 {

    public static void main(String[] args) throws IOException {
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream("linux.words"), "utf-8"))) {
            String line = null;
            long start = System.nanoTime();
            while (true) {
                line = reader.readLine();
                if (line == null) {
                    break;
                }
                line.intern();
            }
            System.out.println("cost:" + (System.nanoTime() - start) / 1000000);
        }


    }
}

Modify VM parameter settings -XX:StringTableSize=200000 The number of hash buckets to set the string pool is 200,000

The fewer hash buckets there are, the greater the probability of hash collisions. A hash bucket needs to mount multiple nodes. When a new string object is added, it traverses through the hash bucket one by one, consuming time.

The more hashes there are, the more scattered the string objects are, and the hash value makes it easy to know if the element is in the string pool.

However, it consumes memory. There is a trade-off between space and time.

Posted by astaroth on Sat, 11 Sep 2021 09:04:06 -0700