/* * Copyright 2003-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package jetbrains.mps.nodeEditor.memory; import org.jetbrains.annotations.NotNull; import java.lang.instrument.Instrumentation; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedList; /** * User: shatalin * Date: 30/12/15 */ public class MemoryAnalyzer { private static final String AGENT_CLASS_NAME = "jetbrains.mps.editor.Agent"; private static final String GET_INSTRUMENTATION_METHOD_NAME = "getInstrumentation"; static Instrumentation ourInstrumentation = getInstrumentation(); private static Instrumentation getInstrumentation() { try { Class<?> agentClass = Class.forName(AGENT_CLASS_NAME, true, ClassLoader.getSystemClassLoader()); if (agentClass == null) { return null; } Method getter = getInstrumentationGetter(agentClass); return (Instrumentation) getter.invoke(null); } catch (ClassNotFoundException e) { } catch (InvocationTargetException e) { } catch (IllegalAccessException e) { } return null; } private static Method getInstrumentationGetter(Class agentClass) { for (Method method : agentClass.getMethods()) { if (GET_INSTRUMENTATION_METHOD_NAME.equals(method.getName())) { return method; } } return null; } private long myTotalSize = 0; public boolean isValid() { return ourInstrumentation != null; } public long getSize() { return myTotalSize; } public void appendObject(Object o) { myTotalSize += getObjectSize(o); } public void appendCollection(Collection c) { if (c instanceof LinkedList) { appendLinkedList((LinkedList) c); } else if (c instanceof ArrayList) { appendArrayList((ArrayList) c); } else { // TODO: support other collections here appendObject(c); } } public void appendLinkedList(LinkedList list) { appendObject(list); Field entryField = getFirstNonPrimitiveField(list.getClass()); if (entryField != null) { appendField(list, entryField, list.size()); } else { // approximation of the overhead for entries in LinkedList myTotalSize += list.size() * 24; } } public void appendArrayList(ArrayList list) { appendObject(list); Field entryField = getFirstNonPrimitiveField(list.getClass()); if (entryField != null) { appendField(list, entryField, 1); } } public void appendFirstNonPrimitiveField(Object o) { Field field = getFirstNonPrimitiveField(o.getClass()); if (field != null) { appendField(o, field, 1); } } private Field getFirstNonPrimitiveField(Class cls) { for (Field nextField : cls.getDeclaredFields()) { if (!nextField.getType().isPrimitive()) { return nextField; } } return null; } public void appendField(Object o, String fieldName) { Field field = null; for (Class<?> clazz = o.getClass(); clazz != null && field == null; clazz = clazz.getSuperclass()) { try { field = clazz.getDeclaredField(fieldName); } catch (NoSuchFieldException e) { } } assert field != null : "Field \"" + fieldName + "\" was not found in class: " + o.getClass(); appendField(o, field, 1); } private void appendField(Object o, @NotNull Field field, int counter) { field.setAccessible(true); try { Object value = field.get(o); if (value != null) { myTotalSize += getObjectSize(value) * counter; } } catch (IllegalAccessException e) { assert false : "Should never happen, message: " + e.getMessage(); } } private long getObjectSize(Object o) { return ourInstrumentation.getObjectSize(o); } }