aragozin/jvm-tools

String fix for Java 10 (PR 35)

stefan-reich opened this issue · 2 comments

Hi,

I am using your library in a Java-based OS to find memory leaks automatically.

One small thing. I believe the library is not fully adapted to Java 10? It generally seems to work, don't get me wrong 👍

The issue I have is with the method org.gridkit.jvmtool.heapdump.HeapWalker.stringValue():

    public static String stringValue(Instance obj) {
        if (obj == null) {
            return null;
        }
        if (!"java.lang.String".equals(obj.getJavaClass().getName())) {
            throw new IllegalArgumentException("Is not a string: " + obj.getInstanceId() + " (" + obj.getJavaClass().getName() + ")");
        }
        char[] text = null;
        int offset = 0;
        int len = -1;
        for(FieldValue f: obj.getFieldValues()) {
            Field ff = f.getField();
            if ("value".equals(ff.getName())) {
                PrimitiveArrayInstance chars = (PrimitiveArrayInstance) ((ObjectFieldValue)f).getInstance();
                text = new char[chars.getLength()];
                for(int i = 0; i != text.length; ++i) {
                    text[i] = ((String)chars.getValues().get(i)).charAt(0);
                }
            }
            // fields below were removed in Java 7
            else if ("offset".equals(ff.getName())) {
                offset = Integer.valueOf(f.getValue());
            }
            else if ("count".equals(ff.getName())) {
                len = Integer.valueOf(f.getValue());
            }
        }

        if (len < 0) {
            len = text.length;
        }

        return new String(text, offset, len);
    }

which clearly is pre-JDK 10 code. Any intention to upgrade this?

Many greetings,
Stefan
BotCompany.de

Edit: In fact, I might just do this myself...

OK, here it goes. Want pull request?

static String stringValue_java10(Instance obj) {
  if (obj == null) return null;
  
  if (!"java.lang.String".equals(obj.getJavaClass().getName()))
    throw new IllegalArgumentException("Is not a string: " + obj.getInstanceId() + " (" + obj.getJavaClass().getName() + ")");
    
  int UTF16 = 1;
  Boolean COMPACT_STRINGS = (Boolean) ( obj.getJavaClass().getValueOfStaticField("COMPACT_STRINGS"));
  if (COMPACT_STRINGS == null)
    return HeapWalker.stringValue(obj); // We're pre Java 9
    
  Object valueInstance = obj.getValueOfField("value");
  PrimitiveArrayInstance chars = (PrimitiveArrayInstance) ( valueInstance);
  
  byte coderField = (byte) ( obj.getValueOfField("coder"));
  byte coder = COMPACT_STRINGS ? coderField : (byte) UTF16;
  int len = chars.getLength() >> coder;
  char[] text = new char[len];
  
  List<String> values = (List) chars.getValues();
  if (coder == UTF16)
    for (int i = 0; i < text.length; i++)
      text[i] = values.get(i).charAt(0);
  else
    for (int i = 0; i < text.length; i++)
      text[i] = (char) Integer.parseInt(values.get(i));
 
  return new String(text);
}

Method reverts to Java 8 method automatically if it's not a Java 10 dump.

Pull request: #35