Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to print the whole String pool?

Tags:

java

string

I wanted to print the whole string pool which contains literals and String objects added using intern() just before garbage collection.

Is there a method implicit to JDK for such operation? How can we inspect the string pool?

like image 810
Prashant Avatar asked Feb 28 '14 11:02

Prashant


People also ask

Where is the string pool stored?

As the name suggests, String Pool in java is a pool of Strings stored in Java Heap Memory.

What is the string constant pool?

The String constant pool is a special memory area. When we declare a String literal, the JVM creates the object in the pool and stores its reference on the stack. Before creating each String object in memory, the JVM performs some steps to decrease the memory overhead.

What is the string pool?

String pool is a storage space in the Java heap memory where string literals are stored. It is also known as String Constant Pool or String Intern Pool. It is privately maintained by the Java String class. By default, the String pool is empty.

Can we clear string pool memory by programming?

No, typically you can not "destroy reference from String pool in Java" manually. The main reason I suppose why you are targeting it is to avoid out of memory errors. In Java 6 days all interned strings were stored in the PermGen – the fixed size part of heap mainly used for storing loaded classes and string pool.


1 Answers

EDIT: The comment suggests that there may be a misunderstanding regarding what this "hack" does. It prints the strings that have been interned by (directly or indirectly) calling intern(), as described in the question. It will not print the "whole string pool", as the string pool only resides in the JVM, is filled with symbols and strings that appear during classloading and initialization, and not accessible from Java side.


NeplatnyUdaj mentioned in a comment that it might be possible to define a new java.lang.String class and sneak this into the JVM at startup. I was curious, and tried it out. And what should I say: It works!

1. Create a new project that contains the package java.lang

2. Insert a class like this into this package

package java.lang;

import java.util.LinkedHashSet;
import java.util.Set;

public class StringPool {

    private static Set<String> pool = null;
    public static synchronized void store(String string)
    {
        try
        {
            if (pool == null)
            {
                pool = new LinkedHashSet<String>();
            }
            pool.add(string);
        }
        catch (Exception e)
        {
            // Ignore
        }
    }

    public static synchronized Set<String> getPool()
    {
        return new LinkedHashSet<String>(pool);
    }

}

3. Copy & Paste the original java.lang.String class into this package. Surprisingly, this works without many problems. It will complain about a single function, namely a call to

    h = sun.misc.Hashing.murmur3_32(HASHING_SEED, value, 0, value.length);

that can safely be replaced with

    h = 0;

4. Change the String#intern() method of the new String class. Originally, this is a native method. It can be replaced with something like

public String intern()
{
    StringPool.store(this);
    return this;
}

5. Create a .JAR file from this project, and store it, for example, as newString.jar

6. Create another project with a test class that generates/contains/uses some strings. (that should be easy) and compile this class, which may be named NewStringTest

7. Launch the test program with the modified string class:

java -Xbootclasspath:newString.jar;C:\jre\lib\rt.jar NewStringTest

The StringPool#getPool() method can then be used to obtain the pool containing the interned strings.



I just tested this with the following class, which manually creates some strings, and some Swing components (which can be expected to contain some strings):

import java.lang.reflect.InvocationTargetException;

import javax.swing.JFrame;
import javax.swing.JTable;
import javax.swing.SwingUtilities;


public class NewStringTest 
{
    public static void main(String[] args) 
    {
        generateSomeStrings();
        System.out.println(StringPool.getPool());
    }

    private static void generateSomeStrings()
    {
        String s = "This is some test string";
        for (int i=0; i<10; i++)
        {
            String t = s + i;
            t.intern();
        }
        try 
        {
            SwingUtilities.invokeAndWait(new Runnable() 
            {
                @Override
                public void run() {
                    JFrame frame = new JFrame();
                    JTable table = new JTable();
                }
            });
        } 
        catch (InvocationTargetException e) 
        {
            e.printStackTrace();
        } 
        catch (InterruptedException e) 
        {
            e.printStackTrace();
        }
    }
}

And the output is

[hashSeed, value, buf, J, D, Z, seed, segmentShift, segmentMask, segments, state, head, tail, waitStatus, next, Ljava/lang/String;, I, [C, [J, Ljava/util/Hashtable;, Ljava/security/PermissionCollection;, Ljava/util/Vector;, Ljava/lang/Class;, main, This is some test string0, This is some test string1, This is some test string2, This is some test string3, This is some test string4, This is some test string5, This is some test string6, This is some test string7, This is some test string8, This is some test string9, INSTANCE, es, , ES, sv, SE, values, Ljava/lang/Object;, [Ljava/awt/Component;, Ljava/awt/LayoutManager;, Ljava/awt/LightweightDispatcher;, Ljava/awt/Dimension;, createUI, invoke, VK_F10, VK_CONTEXT_MENU, VK_SPACE, VK_LEFT, VK_KP_LEFT, VK_RIGHT, VK_KP_RIGHT, VK_ESCAPE, VK_C, VK_V, VK_X, VK_COPY, VK_PASTE, VK_CUT, VK_INSERT, VK_DELETE, VK_DOWN, VK_KP_DOWN, VK_UP, VK_KP_UP, VK_HOME, VK_END, VK_PAGE_UP, VK_PAGE_DOWN, VK_TAB, VK_ENTER, VK_A, VK_SLASH, VK_BACK_SLASH, VK_F2, VK_F8]

like image 108
Marco13 Avatar answered Oct 20 '22 16:10

Marco13