How to compare 2 classes which are loaded from 2 different classloader - java

here is my case:
classloader A, loaded one class("Class1");
then, I changed Class1.java and compile it.
next I loaded Class1.class again by classloader B.
I want to compare these 2 classes, check whether the class meta data changed by someone.
Is there any way to compare 2 classes' definition data?

I am not entirely sure what you mean by "the class meta data" beyond what you can find through the reflection APIs. Here is an attempt to answer the question based on my best guess.
By definition data do you mean their declared internal variables and method signatures? Because you can do that with reflection (getDeclaredMethod() and getDeclaredFields()). However, if the two classes are loaded from different class loaders, they will not be equal (see the Class javadocs on equality), even if they are loaded from the same compiled bytecode.
There is other information you can get from the Reflection APIs, including what class it inherits from, what interfaces it implements, and any Annotations that are compiled in with it (assuming 1.5 or higher of course).
You could also potentially do a hash of the Class files (finding them through the classloader is possible) and see if they are different - that would tell you if they had different code in them.
Hope that helps.

thanks!
Reflection could collect one class's meta data, but it's hard to check whether one class is changed.
I can locate that class file, but also it's hard to check whether one class is changed.
I assumed there should be a way to check loaded classes, whether they have the same data(from the same java file).

Related

How much space does a class take in the JVM?

I've been looking to no avail searching different sites because all the search results want to tell me how to compute the size of an object, not of a class.
When we define a class (with a .class file in a jar/war) and it gets loaded into the JVM, how much space does that take up? Obviously it depends on the actual things in the class, a class with more fields has more metadata to store. But if we say for example the class had 10 integer fields and 10 reference fields to other objects:
How much extra space would it take up in the JVM to have say, 1,000 of those classes (all extending the same base class)?
Would it change if they were anonymous classes instead of defined classes?
Well every Class itself is an object. So an indicative size would be to call Instrumentation.getObjectSize() on the class itself.
You can follow the tutorial here:
https://www.baeldung.com/java-size-of-object
And then if you want to check the size of MyClass you can do:
InstrumentationAgent.getObjectSize(MyClass.getClass());
This doesn't mean it is the only memory associated with this class, because the class loader, garbage collector and other internal workings of the JVM might keep other meta information about the class.
The answer will most definitely depend on JVM implementation (including version of such implementation, platform etc). There is no one number you can get.
As for anonymous inner classes, these still have class names generated for them, so this should have zero impact.

When I use java.lang.instrument.Instrumentation#redefineClasses(), should I pass an array to the function?

I used java.lang.instrument.Instrumentation#redefineClasses() to redefine existing classes. Sometimes, I need to redefine several classes.
If I redefine classes one by one, I will know which ones were successful and which ones failed.
But is it better to put classes redefined in an array together to get more correctness?
If I redefine class one by one, I will know which is success, which is
failed.
True.
But is it better to put classes redefined in an array together to get
more correctness?
I didn't get what you meant by more correctness! But, anyways from my understanding, using a set(array) of classes can be particularly helpful in the case where there is an interdependence of one class on other class. So, in this case you can re-define both classes by passing them in this method.
Also, Java Documentation of Interface Instrumentation says :
This method is used to replace the definition of a class without
reference to the existing class file bytes, as one might do when
recompiling from source for fix-and-continue debugging.
...
This method operates on a set in order to allow interdependent changes to more than one class at the same time (a redefinition of
class A can require a redefinition of class B).
But, do keep remember :
If this method throws an exception, no classes have been redefined.
It is much more performant to instrument classes in a batch compared to passing each class individually.
The JVM needs to halt the application for applying the redefinition which is a rather costly operation so it is worth grouping up.
Also, grouping allows for interdependent changes of classes.

Java collect all used classes from bytecode

I trying to implement a RemoteClassLoader which copy and load all classes which will be used in runtime. First I need to collect the used Classes, I found a solution:
Find out which classes of a given API are used
but this is not exactly what a need, it collect only the "visible" class usages, just like loading the class and iterating all of declared field and methods, and collecting all types.
I have a class which contains only static methods, instance of this method is not used, so it will be never given to a function or will be a filed, and so I can't see that class.
Naturally the bytecode file contains the name of this class:
strings TestClass.class | grep -i "json"
gives: org/json/JSONObject
And yes that class I search and not fond.
How can I find it? And the others which I use only in functions.
The easiest, albeit conservative method is to simply take all of the Class_info entries from the constant pool. In order to call a method or access a field of a class, there must be a constant pool entry for that class (not counting reflection and not counting overriding methods in subclasses).
There are a number of tools out there that will parse a classfile and give you access to this. Reflection of course is much harder, and in general undecideable.
Edit: This won't include type descriptors, which are just Utf8_infos. If you want to find classes used as types as well, there are two approaches. Either you can go through all the Utf8s and include everything that looks like a descriptor (which may have false positives in rare cases), or you can go through the classfile and find all the type descriptor references.

JAVA bytecode optimization

This is a basic question.
I have code which shouldn't run on metadata beans. All metadata beans are located under metadata package.
Now,
I use reflection API to find out whether a class is located in the the metadata package.
if (newEntity.getClass().getPackage().getName().contains("metadata"))
I use this If in several places within this code.
The question is: Should I do this once with:
boolean isMetadata = false
if (newEntity.getClass().getPackage().getName().contains("metadata")) {
isMetadata = true;
}
C++ makes optimizations and knows that this code was already called and it won't call it again. Does JAVA makes optimization? I know reflection API is a beat heavy and I prefer
not to lose expensive runtime.
You should of course check whether there really is a performance issue before putting any work into optimising. getClass() is probably quite fast already (faster than instanceof, anyway). You could probably cache the set of classes that are in the metadata package so you don't need to keep checking the package names.
If you really need to compare packages, you could find the metadata package once, using the Package.getPackage(String name) method, then for each object, call getClass().getPackage() as before, and compare the two package objects.
This is quicker and more elegant than checking for a string in the package name, but would probably not work correctly if there are multiple classloaders, as the Package objects wouldn't be equal (==) and Package doesn't over-ride .equals(). Thinking about it, it may not even be guaranteed to work on a single classloader, but I suspect that in practice you get the same Package instance rather than another copy - would be wise to check this first!, e.g:
String.class.getPackage() == Integer.class.getPackage() // should be true
Update if you check the source code for Class.getPackage(), Package.getPackage() and ClassLoader.getPackage() you can see that they cache the Package objects, so you should be safe comparing them when using a single classloader
One problem of a package-naming convention is that you have to enforce and maintain it throughout the codebase, which could become a maintenance problem over time. A more explicit way of identifying the classes might be better.
Alternative approaches to identify specific groups of classes include:
Making your metadata beans implement a marker interface
Using Java Annotations to mark metadata beans
Making all beans implement a common interface with a method that can be called to check whether the are in a specific category that you define. This is ugly as it's basically duplicating the type system, but would be fast since it doesn't need reflection.

Case sensitivity of Java class names

If one writes two public Java classes with the same case-insensitive name in different directories then both classes are not usable at runtime. (I tested this on Windows, Mac and Linux with several versions of the HotSpot JVM. I would not be surprised if there other JVMs where they are usable simultaneously.) For example, if I create a class named a and one named A like so:
// lowercase/src/testcase/a.java
package testcase;
public class a {
public static String myCase() {
return "lower";
}
}
// uppercase/src/testcase/A.java
package testcase;
public class A {
public static String myCase() {
return "upper";
}
}
Three eclipse projects containing the code above are available from my website.
If try I calling myCase on both classes like so:
System.out.println(A.myCase());
System.out.println(a.myCase());
The typechecker succeeds, but when I run the class file generate by the code directly above I get:
Exception in thread "main" java.lang.NoClassDefFoundError: testcase/A (wrong name: testcase/a)
In Java, names are in general case sensitive. Some file systems (e.g. Windows) are case insensitive, so I'm not surprised the above behavior happens, but it seems wrong. Unfortunately the Java specifications are oddly non-commital about which classes are visible. The Java Language Specification (JLS), Java SE 7 Edition (Section 6.6.1, page 166) says:
If a class or interface type is declared public, then it may be accessed by
any code, provided that the compilation unit (ยง7.3) in which it is declared is
observable.
In Section 7.3, the JLS defines observability of a compilation unit in extremely vague terms:
All the compilation units of the predefined package java and its subpackages lang
and io are always observable. For all other packages, the host system determines which compilation units are observable.
The Java Virtual Machine Specification is similarly vague (Section 5.3.1):
The following steps are used to load and thereby create the nonarray class or
interface C denoted by [binary name] N using the bootstrap class loader [...]
Otherwise, the Java virtual machine passes the argument N to an invocation of a
method on the bootstrap class loader to search for a purported representation of C
in a platform-dependent manner.
All of this leads to four questions in descending order of importance:
Are there any guarantees about which classes are loadable by the default class loader(s) in every JVM? In other words, can I implement a valid, but degenerate JVM, that won't load any classes except those in java.lang and java.io?
If there are any guarantees, does the behavior in the example above violate the guarantee (i.e. is the behavior a bug)?
Is there any way to make HotSpot load a and A simultaneously? Would writing a custom class loader work?
Are there any guarantees about which classes are loadable by the bootstrap class loader in every JVM?
The core bits and pieces of the language, plus supporting implementation classes. Not guaranteed to include any class that you write. (The normal JVM loads your classes in a separate classloader from the bootstrap one, and in fact the normal bootstrap loader loads its classes out of a JAR normally, as this makes for more efficient deployment than a big old directory structure full of classes.)
If there are any guarantees, does the behavior in the example above violate the guarantee (i.e. is the behavior a bug)?
Is there any way to make "standard" JVMs load a and A simultaneously? Would writing a custom class loader work?
Java loads classes by mapping the full name of the class into a filename that is then searched for on the classpath. Thus testcase.a goes to testcase/a.class and testcase.A goes to testcase/A.class. Some filesystems mix these things up, and may serve the other up when one is asked for. Others get it right (in particular, the variant of the ZIP format used in JAR files is fully case-sensitive and portable). There is nothing that Java can do about this (though an IDE could handle it for you by keeping the .class files away from the native FS, I don't know if any actually do and the JDK's javac most certainly isn't that smart).
However that's not the only point to note here: class files know internally what class they are talking about. The absence of the expected class from the file just means that the load fails, leading to the NoClassDefFoundError you received. What you got was a problem (a mis-deployment in at least some sense) that was detected and dealt with robustly. Theoretically, you could build a classloader that could handle such things by keeping searching, but why bother? Putting the class files inside a JAR will fix things far more robustly; those are handled correctly.
More generally, if you're running into this problem for real a lot, take to doing production builds on a Unix with a case-sensitive filesystem (a CI system like Jenkins is recommended) and find which developers are naming classes with just case differences and make them stop as it is very confusing!
Donal's fine explanation leaves little to add, but let me briefly muse on this phrase:
... Java classes with the same case-insensitive name ...
Names and Strings in general are never case-insensitive in themselves, it's only there interpretation that can be. And secondly, Java doesn't do such an interpretation.
So, a correct phrasing of what you had in mind would be:
... Java classes whose file representations in a case-insensitive file-system have identical names ...
I tried to add or remove a character from one of the class names and it worked. I feel it's always better to use different class names.
Don't think just about folders.
Use explicit different namespaces ("packages") for your classes, and maybe use folders to match your classes.
When I mention "packages", I don't mean "*.JAR" files, but, just the concept of:
package com.mycompany.mytool;
// "com.mycompany.mytool.MyClass"
public class MyClass
{
// ...
} // class MyClass
When you do not specify a package for your code, the java tools (compiler, I.D.E., whatever), assume to use the same global package for all. And, in case of several similar classes, they have a list of folders, where to look for.
Packages are like "virtual" folders in your code, and apply to all your packages on your classpath, or installation of Java. You can have several classes, with the same I.D., but, if they are in different package, and you specify which package to look for, you won't have any problem.
Just my 2 cents, for your cup of Java coffe

Categories