Negative Impact on ClassLoader

Joe

Thành viên VIP
21/1/13
2,898
1,293
113
Hi

Integrated Development Environment (IDE) Software such as Ellipse, Netbeans, etc. is supposed for power users who have to develop big Software package. It eases the writing chores by auto-creating some writing peripheries (e.g. folders, empty classes, proposed methods, etc.) And, as said, POWER USERS who are usually good developers with years-long experience. For newbies or plain developers IDE is a very bad developing tool. It obfuscates them with some hidden auto-settings so that they, the newbies or mediocre developers, fail to understand when they confront some intricate problems (i.e. exceptions) OR when they have to work without their familiar IDE.

The biggest hidden problem is the sheer number of "little" classes or objects. Object Oriented Programming (OOP) is not the same with "many" little sub-objects for an object, but the art how to group diverse things into some common objects. The more objects have to be loaded the slower the app works. In worst case it could last too long for a user who has to work with the app. The reason is in Computer Science: the Dynamic Link.

What is the Dynamic Link?
In the past (60/70 era) programs (or apps) had to be compiled and linked together so that they could run. If you are/were a C-Programmer you know what the "makefile" and the linker mean


Pic.1 (source: https://www.sitesbay.com)

The problem of such "static" link is the redundancy of the same codes (e.g. math. subprograms like sine, cosine, etc.) which are embedded in the apps and could cause in some cases memory shortage and therewith an unexpected performance problem. Instead of fastness is a slowness of the (entire) system. To solve such redundancy-problems Dynamic Link was introduced in the 80s where reusable subprograms are grouped into different libraries which are then shared by all different apps. The result is a reduction of wasted memory and the apps become more flexible. The trade-off is the latency of dynamic-loading. Or in other words: the apps run a "bit" slower than the static-linked apps. The libraries of grouping of codes are suffixed by .dll (Dynamic Link Library on Windows) or .so (Shared Objects on Linux and some UNIX) or .sl (Shared Library on HP-UX). On Java Virtual Machine it's the JARred files or all objects in the CLASSPATH.


Pic.2 (source: https://pussgrc.github.io)

Pic.2 shows you the working algorithm for an application:
  • Build: the sources are developed and compiled. The Link Edit is here for JAVA the building of a JAR or a DLL/SL/SO for C
  • Run: the main codes run and the needed subprograms (here: objects) are loaded on demand.
You may now perceive what problems would arise if there are "numerous" little classes (or objects) have to be loaded when they are needed. Second by second the loading time sums up to minutes, then to hours...

And that is the real problem when newbies or mediocre developers code an application: too many little classes and too numerous dependencies (on different libraries/directories) when they use IDE and create senselessly numerous little APIs. The result is that the OS is busying with loading and locating the dependency-paths. The users suffer and the productivity sinks. The problems could worsen if mediocre coders develop Software with framework such as Spring-Boot or whatever framework.

The following examples show you the significance of the single loading or multiple loading (see sort.zip).
  • Sort.java is an API with TWO different sorting techniques: ForkJoinPool and Excecutor's StealingPool
  • ForkPoolSort.java is an API with ONE sorting with ForkJoinPool
  • StealingPoolSort.java is an API with ONE sorting with ExecutorsStealingPool

The apps:
  • AllSort.java runs the Sorting with system-ClassLoading
  • XSort.java runs the sorting with System-ClassLoading for ForkPoolSort and StealingPoolSort

The results show you that the latency of "big" API (AllSort.java) is less than the one of 2 "little" APIs (XSort.java)

AllSort.java with ONE API Sort.java of 2 Sorting methods (fSort and sSort)
PHP:
// Joe Nartca (C)
import joe.Sort;
public class AllSort {
  public static void main(String... a) {
    try {
      byte[] cont = Files.readAllBytes((new File(a.length>0?a[0]:"testFile.txt")).toPath());
      // do the JoeSorting....
      String big = (new String(cont)).replaceAll("\r","");
      String[] ss, sa = Arrays.stream(big.split("\n"))
                .filter(s -> (s != null && s.length() > 0))
                .toArray(String[]::new);
      long beg = System.nanoTime();
      ss = Sort.fSort(sa);
      System.out.println("\nForkPoolSort:"+((double)(System.nanoTime()-beg)/1000)+
                 " microSec. (systemLoad)");
      ss = Sort.sSort(sa);
      System.out.println("\nStealingPoolSort:"+((double)(System.nanoTime()-beg)/1000)+
                 " microSec. (systemLoad)");
    } catch (Exception ex) {
      ex.printStackTrace();
    }
  }
}
XSort.java with TWO APIs: ForkPoolSort.java and StealingPoolSort.java of ONE method per API
PHP:
// Joe Nartca (C)
import joe.ForkPoolSort;
import joe.StealingPoolSort;
public class XSort {
  public static void main(String... a) {
    try {
      byte[] cont = Files.readAllBytes((new File(a.length>0?a[0]:"testFile.txt")).toPath());
      // do the JoeSorting....
      String big = (new String(cont)).replaceAll("\r","");
      String[] ss, sa = Arrays.stream(big.split("\n"))
                .filter(s -> (s != null && s.length() > 0))
                .toArray(String[]::new);
      long beg = System.nanoTime();
      ss = ForkPoolSort.sort(sa);
      System.out.println("\nForkPoolSort:"+((double)(System.nanoTime()-beg)/1000)+
                 " microSec. (systemLoad)");
      ss = StealingPoolSort.sort(sa);
      System.out.println("\nStealingPoolSort:"+((double)(System.nanoTime()-beg)/1000)+
                 " microSec. (systemLoad)");
    } catch (Exception ex) {
      ex.printStackTrace();
    }
  }
}
The result:
Code:
C:\links\java\classloader>javac -g:none -d ./classes *.java
Note: Some input files use or override a deprecated API.
Note: Recompile with -Xlint:deprecation for details.

C:\links\java\classloader>java AllSort

ForkPoolSort:12713.001 microSec. (systemLoad)

StealingPoolSort:47342.601 microSec. (systemLoad)

C:\links\java\classloader>java XSort

ForkPoolSort:13984.1 microSec. (systemLoad)

StealingPoolSort:61082.199 microSec. (systemLoad)

C:\links\java\classloader>
Joe
 

Attachments

Sửa lần cuối:

Joe

Thành viên VIP
21/1/13
2,898
1,293
113
(cont.)
To show you the impact of Object/ClassLoading on the execution performance I have modified AllSort.java and XSort.java by Loading-on-demand as following:

All_Sort.java with userLoading.
PHP:
public class All_Sort {
  public static void main(String... a) {
    try {
      byte[] cont = Files.readAllBytes((new File(a.length>0?a[0]:"testFile.txt")).toPath());
      // do the JoeSorting....
      String big = (new String(cont)).replaceAll("\r","");
      String[]ss, sa = Arrays.stream(big.split("\n"))
                .filter(s -> (s != null && s.length() > 0))
                .toArray(String[]::new);
      // userLoading HERE
      joe.Sort joe = (joe.Sort)Class.forName("joe.Sort")
                                    .newInstance();
      //
      long beg = System.nanoTime();
      ss = joe.fSort(sa);
      System.out.println("\nForkPoolSort:"+((double)(System.nanoTime()-beg)/1000)+
                 " microSec. (userLoad)");
      ss = joe.sSort(sa);
      System.out.println("\nStealingPoolSort:"+((double)(System.nanoTime()-beg)/1000)+
                 " microSec. (userLoad)");
    } catch (Exception ex) {
      ex.printStackTrace();
    }
  }
}
The result. The Loading time is for example: 14795.9 (incl. loading with systemLoad) and 12545.3 (excl. loading as userLoading)
Code:
C:\links\java\classloader>java All_Sort

ForkPoolSort:12545.3 microSec. (userLoad)

StealingPoolSort:50047.9 microSec. (userLoad)

C:\links\java\classloader>java AllSort

ForkPoolSort:14795.9 microSec. (systemLoad)

StealingPoolSort:66876.5 microSec. (systemLoad)

C:\links\java\classloader>
X_Sort.java with userLoading
PHP:
public class X_Sort {
  public static void main(String... a) {
    try {
      byte[] cont = Files.readAllBytes((new File(a.length>0?a[0]:"testFile.txt")).toPath());
      // do the JoeSorting....
      String big = (new String(cont)).replaceAll("\r","");
      String[] ss, sa = Arrays.stream(big.split("\n"))
                .filter(s -> (s != null && s.length() > 0))
                .toArray(String[]::new);
      // userLoading
      joe.ForkPoolSort joe = (joe.ForkPoolSort)Class.forName("joe.ForkPoolSort")
                                                    .newInstance();
      //
      long beg = System.nanoTime();
      ss = joe.sort(sa);
      System.out.println("\nForkPoolSort:"+((double)(System.nanoTime()-beg)/1000)+
                 " microSec. (userLoad)");
      // userLoading
      joe.StealingPoolSort Joe = (joe.StealingPoolSort)Class.forName("joe.StealingPoolSort")
                                                            .newInstance();
      //
      ss = Joe.sort(sa);
      System.out.println("\nStealingPoolSort:"+((double)(System.nanoTime()-beg)/1000)+
                 " microSec. (userLoad)");
    } catch (Exception ex) {
      ex.printStackTrace();
    }
  }
}
The execution: again without the ClassLoading (X_Sort as userLoad) the processing time is shorter than with the ClassLoading (XSort/systemLoading).
Code:
C:\links\java\classloader>java XSort

ForkPoolSort:11934.8 microSec. (systemLoad)

StealingPoolSort:46918.6 microSec. (systemLoad)

C:\links\java\classloader>java X_Sort

ForkPoolSort:11798.7 microSec. (userLoad)

StealingPoolSort:46419.7 microSec. (userLoad)

C:\links\java\classloader>
 

Joe

Thành viên VIP
21/1/13
2,898
1,293
113
(cont.)
When the testing codes of XSort.java
PHP:
      long beg = System.nanoTime();
      ss = ForkPoolSort.sort(sa);
      System.out.println("\nForkPoolSort:"+((double)(System.nanoTime()-beg)/1000)+
                 " microSec. (systemLoad)");
are at the line ss = ForkPoolSort.sort(sa) JVM firstly invokes ClassLoader to load the API ForkPoolSort and that naturally takes some time before the method sort() is executed while the codes of X_Sort.java
PHP:
      joe.ForkPoolSort joe = (joe.ForkPoolSort)Class.forName("joe.ForkPoolSort")
                                                    .newInstance();
      long beg = System.nanoTime();
      ss = joe.sort(sa);
      System.out.println("\nForkPoolSort:"+((double)(System.nanoTime()-beg)/1000)+
                 " microSec. (userLoad)");
excludes the loading time (long beg = System.nanoTime() is invoked after ClassLoading). The time difference between them is the latency or the loading time whenever a Class (or API) is loaded or reloaded (because JVM Garbage Collector removed it.)

Conclusion: the more an app has to load little APIs (classes) the more the latency sums up. And that is the negative impact of TOO MANY little APIs or Classes which have to be loaded during the runtime. Rule: don't be naive to believe that "many" APIs will automatically make your app better or easier to maintain. But on the contrary: they obfuscate the global view on an object and slow down the THROUGHPUT of your app.

Note: the invocation "
newInstance()" is deprecated and should be replaced by "getDeclaredConstructor().newInstance()"