-
Notifications
You must be signed in to change notification settings - Fork 10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Retained size by ClassLoaders #35
base: master
Are you sure you want to change the base?
Changes from 10 commits
9cdbad5
6ce9b73
ebbd561
c15a6af
881e5b6
3338af1
a33d6b0
17a208e
a99104a
000ef33
29289a0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. | ||
|
||
#include <vector> | ||
#include "retained_size_by_classloaders.h" | ||
#include <algorithm> | ||
#include <numeric> | ||
|
||
|
||
jvmtiError RetainedSizeByClassLoadersAction::tagObjectsByClassLoader(jclass *classes, jint* cnt, jobject classLoader, jsize classLoaderIndex) { | ||
jvmtiError err = JVMTI_ERROR_NONE; | ||
std::vector<jclass> filteredClasses; | ||
for (jsize i = 0; i < *cnt; i++) { | ||
jobject rootClassLoader = getClassLoader(env, classes[i]); | ||
if (!env->IsSameObject(rootClassLoader, NULL)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here we can combine these conditions:
|
||
if (isEqual(env, classLoader, rootClassLoader)) { | ||
filteredClasses.emplace_back(classes[i]); | ||
} | ||
} | ||
} | ||
|
||
err = tagObjectsOfClassLoaderClasses(toJavaArray(env, filteredClasses), classLoaderIndex); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Lets rewrite it like this:
|
||
if (!isOk(err)) return err; | ||
if (shouldStopExecution()) return MEMORY_AGENT_INTERRUPTED_ERROR; | ||
return err; | ||
} | ||
|
||
jlong RetainedSizeByClassLoadersAction::tagOtherObjects(jclass *classes, jint* cnt, jobjectArray classLoadersArray) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe this function should return |
||
jvmtiError err = JVMTI_ERROR_NONE; | ||
std::vector<jclass> filteredClasses; | ||
for (jsize i = 0; i < *cnt; i++) { | ||
jobject rootClassLoader = getClassLoader(env, classes[i]); | ||
if (!env->IsSameObject(rootClassLoader, NULL)) { | ||
for (jsize j = 0; j < env->GetArrayLength(classLoadersArray); j++) { | ||
if (isEqual(env, env->GetObjectArrayElement(classLoadersArray, j), rootClassLoader)) { | ||
filteredClasses.emplace_back(classes[i]); | ||
break; | ||
} | ||
} | ||
} | ||
} | ||
|
||
std::vector<jclass> nonfilteredClasses; | ||
for (jsize i = 0; i < *cnt; i++) { | ||
if (std::find(filteredClasses.begin(), filteredClasses.end(), classes[i]) == filteredClasses.end()) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be much better to store filtered classes in |
||
nonfilteredClasses.emplace_back(classes[i]); | ||
} | ||
} | ||
std::cout << "SIZE: " << nonfilteredClasses.size() << std::endl; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use the logger instead |
||
|
||
err = tagObjectsOfClassLoaderClasses(toJavaArray(env, nonfilteredClasses), env->GetArrayLength(classLoadersArray)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} | ||
|
||
jlong RetainedSizeByClassLoadersAction::getHeapSize(){ | ||
jlong totalSize = 0; | ||
IterateThroughHeap(0, nullptr, countHeapSize, &totalSize, | ||
"calculate heap size"); | ||
return totalSize; | ||
} | ||
|
||
RetainedSizeByClassLoadersAction::RetainedSizeByClassLoadersAction(JNIEnv *env, jvmtiEnv *jvmti, jobject object) | ||
: RetainedSizeAction(env, jvmti, object) { | ||
|
||
} | ||
|
||
jvmtiError RetainedSizeByClassLoadersAction::getRetainedSizeByClassLoaders(jobjectArray classLoadersArray, | ||
std::vector <jlong> &result) { | ||
jvmtiError err = JVMTI_ERROR_NONE; | ||
jclass *classes; | ||
jint cnt; | ||
err = jvmti->GetLoadedClasses(&cnt, &classes); | ||
std::cout << cnt << std::endl; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be better to use the logger instead of plain stdout |
||
for (jsize i = 0; i < env->GetArrayLength(classLoadersArray); i++) { | ||
jvmtiError err = tagObjectsByClassLoader(classes, &cnt, env->GetObjectArrayElement(classLoadersArray, i), i); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also there's no need to declare a new variable here |
||
} | ||
tagOtherObjects(classes, &cnt, classLoadersArray); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
if (!isOk(err)) return err; | ||
if (shouldStopExecution()) return MEMORY_AGENT_INTERRUPTED_ERROR; | ||
|
||
err = tagHeap(); | ||
if (!isOk(err)) return err; | ||
if (shouldStopExecution()) return MEMORY_AGENT_INTERRUPTED_ERROR; | ||
|
||
result.resize(env->GetArrayLength(classLoadersArray) + 1); | ||
err = IterateThroughHeap(JVMTI_HEAP_FILTER_UNTAGGED, nullptr, visitObject, result.data(), | ||
"calculate retained size"); | ||
return err; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} | ||
|
||
jlongArray RetainedSizeByClassLoadersAction::executeOperation(jobjectArray classLoadersArray) { | ||
std::vector <jlong> result; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. An extra space |
||
jvmtiError err = getRetainedSizeByClassLoaders(classLoadersArray, result); | ||
if (!isOk(err)) { | ||
handleError(jvmti, err, "Could not estimate retained size by classLoaders"); | ||
return env->NewLongArray(0); | ||
} | ||
std::cout << "HEAP SIZE: " << getHeapSize() << std::endl; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since memory agent is a library, it would be better not to pollute stdout, but instead return the result in the format of nested arrays: |
||
std::cout << "CLASSLOADERS CLASSES: " << std::accumulate(result.begin(), result.end()-1, 0) <<std::endl; | ||
std::cout << "OTHER CLASSES: " << result.back() <<std::endl; | ||
result.pop_back(); | ||
return toJavaArray(env, result); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. | ||
|
||
#ifndef MEMORY_AGENT_RETAINED_SIZE_BY_CLASSLOADERS_H | ||
#define MEMORY_AGENT_RETAINED_SIZE_BY_CLASSLOADERS_H | ||
|
||
#include "../memory_agent_action.h" | ||
#include "retained_size_action.h" | ||
#include "retained_size_by_classes.h" | ||
|
||
|
||
class RetainedSizeByClassLoadersAction : public RetainedSizeAction<jlongArray> { | ||
public: | ||
RetainedSizeByClassLoadersAction(JNIEnv *env, jvmtiEnv *jvmti, jobject object); | ||
|
||
private: | ||
jlongArray executeOperation(jobjectArray classLoadersArray) override; | ||
jvmtiError getRetainedSizeByClassLoaders(jobjectArray classLoadersArray, std::vector<jlong> &result); | ||
jvmtiError tagObjectsByClassLoader(jclass *classes, jint* cnt, jobject classLoader, jsize classLoaderIndex); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Asterisks should stick to the variable name |
||
jlong getHeapSize(); | ||
jlong tagOtherObjects(jclass *classes, jint* cnt, jobjectArray classLoadersArray); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Asterisks should stick to the variable name |
||
}; | ||
|
||
jint JNICALL visitObject(jlong classTag, jlong size, jlong *tagPtr, jint length, void *userData); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this function should not be declared here |
||
|
||
|
||
|
||
#endif //MEMORY_AGENT_RETAINED_SIZE_BY_CLASSLOADERS_H |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -49,6 +49,15 @@ jobjectArray toJavaArray(JNIEnv *env, std::vector<jobject> &objects) { | |
return res; | ||
} | ||
|
||
jobjectArray toJavaArray(JNIEnv *env, std::vector <jclass> &objects) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. An extra space |
||
auto count = static_cast<jsize>(objects.size()); | ||
jobjectArray res = env->NewObjectArray(count, env->FindClass("java/lang/Class"), nullptr); | ||
for (auto i = 0; i < count; ++i) { | ||
env->SetObjectArrayElement(res, i, objects[i]); | ||
} | ||
return res; | ||
} | ||
|
||
jlongArray toJavaArray(JNIEnv *env, std::vector<jlong> &items) { | ||
auto count = static_cast<jsize>(items.size()); | ||
jlongArray result = env->NewLongArray(count); | ||
|
@@ -260,6 +269,18 @@ jvmtiError tagClassAndItsInheritors(JNIEnv *env, jvmtiEnv *jvmti, jobject classO | |
return err; | ||
} | ||
|
||
jvmtiError tagClass(JNIEnv *env, jvmtiEnv *jvmti, jobject classObject, std::function<jlong(jlong)> &&createTag) { | ||
jlong oldTag; | ||
jvmtiError err = jvmti->GetTag(classObject, &oldTag); | ||
if (err != JVMTI_ERROR_NONE) return err; | ||
jlong newTag = createTag(oldTag); | ||
if (newTag != 0) { | ||
err = jvmti->SetTag(classObject, newTag); | ||
if (err != JVMTI_ERROR_NONE) return err; | ||
} | ||
return err; | ||
} | ||
|
||
jmethodID getIsAssignableFromMethod(JNIEnv *env) { | ||
jclass langClass = env->FindClass("java/lang/Class"); | ||
return env->GetMethodID(langClass, "isAssignableFrom", "(Ljava/lang/Class;)Z"); | ||
|
@@ -270,6 +291,17 @@ std::string getToString(JNIEnv *env, jobject object) { | |
return jstringTostring(env, reinterpret_cast<jstring>(name)); | ||
} | ||
|
||
jobject getClassLoader(JNIEnv *env, jclass clazz) { | ||
return env->CallObjectMethod(clazz, env->GetMethodID(env->GetObjectClass(clazz), "getClassLoader", | ||
"()Ljava/lang/ClassLoader;")); | ||
} | ||
|
||
bool isEqual(JNIEnv *env, jobject obj, jobject otherObj) { | ||
jclass objClass = env->GetObjectClass(obj); | ||
jmethodID equalsID = env->GetMethodID(objClass, "equals", "(Ljava/lang/Object;)Z"); | ||
return env->CallBooleanMethod(obj, equalsID, otherObj); | ||
} | ||
|
||
ThreadSuspender::ThreadSuspender(jvmtiEnv *jvmti) : jvmti(jvmti) { | ||
jint threadCnt; | ||
jthread *threads; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -97,6 +97,14 @@ public synchronized <T> T[] getAllReachableObjects(Object startObject, Class<T> | |
return resultArray; | ||
} | ||
|
||
/** | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The formatting is a little off here |
||
* TODO | ||
*/ | ||
@SuppressWarnings("unchecked") | ||
public synchronized long[] getRetainedSizeByClassLoaders(ClassLoader[] classloaders) throws MemoryAgentExecutionException { | ||
return (long [])getResult(callProxyMethod(() -> proxy.getRetainedSizeByClassLoaders(classloaders))); | ||
} | ||
|
||
/** | ||
* Adds an allocation listener that catches allocation sampling events for specified classes. | ||
* | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Asterisks should stick to the variable name