Code Monkey home page Code Monkey logo

bytecodeoptimizer's Introduction

Bytecode Optimizer

A Java program to optimize jar files. The optimizer was designed as a part of the Ballerina language platform. However, it can be used for any language that compiles into Java bytecode. The optimizer is capable of removing unused methods and classes in a jar file. It is capable of reducing the size of Ballerina-generated jar files by 40%-65%.

Usage

Extract the optimizer fat jar and run it using the java -jar command. Pass the path to the config file containing config options as an argument.

java -jar optimizer.jar ./optimizer.config

The config file accepts following configuaration options.

inputJar: Path to the jar file that needs to be optimized (mandatory)
outputJar: Path to the jar file the optimized program should be written to (mandatory)
mainMethodClass: Name of the class that contains the main method (mandatory)
noUnusedMethodRemoval: Set to true if the optimizer should remove only unused classes without removing unused methods (optional)
keepClasses: A comma separated list of class names that needs to be preserved by default during the optimization. All these classes and their methods will be preserved in the output jar as they are. (optional)

An example configuration file is shown below.

//optimizer.config

inputJar:input.jar
outputJar:output.jar
mainMethodClass:user/demo/Main
noUnusedMethodRemoval:false
keepClasses:user/demo/KeepClass1,user/demo/KeepClass2,user/demo/KeepClass3

Implementation

To identify unused methods in the jar file, and by extension unused classes, Bytecode Optimizer constructs the callgraph of the given program, using Class Hierarchy Analysis (CHA) algorithm, starting from the main method defined in the entry class. To conduct the analysis, Bytecode Optimizer utilizes ASM's tree-based visitors including ClassNode and MethodNode. On top of the callgraph construction, ASM ClassNode is used to identified field types used in the program that are not captured through the callgraph contstruction.

The current high-level design of the optimizer is as follows. design

The optimization process is carried out in three main steps.

  1. Build class hierarchy
  2. Identify used classes
  3. Identify used methods

Building Class Hierarchy

As the first step, to help the CHA analysis, the program maps the class heirarchy among the class nodes in the graph.

public void buildClassHierarchy() {

    for (String name : nodes.keySet()) {
        ClassGraphNode current = nodes.get(name);
        setSuperNode(current);
        setInterfaces(current);
    }
    for (String name : javaNodes.keySet()) {
        ClassGraphNode current = javaNodes.get(name);
        setJavaSuperNode(current);
        setJavaInterfaces(current);
    }
}

Identifying Used Classes

To identify dependent classes of a used class, ClassNodeVisitor object is passed to each used class starting from the root node, the node which contains the main method. Dependent classes are then recognized as used classes and the process continues until all the used classes in the jar are identified by statically analyzing bytecode.

public void visitNode(ClassGraphNode node) {
    node.markAsVisited();
    node.accept(new ClassNodeVisitor());
    visitDependentNodes(node);
    if (node.isServiceProvider()) {
        visitChildNodes(node);
    }
}

private void visitDependentNodes(ClassGraphNode node) {
    for (String className: node.getDependencies()) {
        if (nodes.get(className) != null && !nodes.get(className).isVisited()) {
            visitNode(nodes.get(className));
        }
    }
}

private void visitChildNodes(ClassGraphNode node) {
    for (ClassGraphNode childNode : node.getChildNodes()) {
        if (!childNode.isVisited()){
            visitNode(childNode);
        }
    }
}

Identifying Used Methods

To identify the used methods, the optimizer builds the callgraph of the program following an approach similar to Class Hierarchy Analysis. The main method in the root node is considered as the entry point to the callgraph construction.

private void findLinkedMethods(ClassGraphNode node) {

    visitNodeForMethods(node);

    for (MethodNode methodNode : node.methods) {
        MethodGraphNode method = (MethodGraphNode) methodNode;

        if (method.isUsed() && !method.isCalledVisited()) {

            method.markAsCalledVisited();
            visitDependencies(method);

            InsnList instructions = method.instructions;

            for (int i = 0; i < instructions.size(); i++) {
                AbstractInsnNode insnNode = instructions.get(i);

                //check if the instruction type is of INVOKE_STATIC, INVOKE_VIRTUAL,
                // INVOKE_SPECIAL, and INVOKE_INTERFACE types
                if (insnNode.getType() == AbstractInsnNode.METHOD_INSN) {
                    visitMethodInsn((MethodInsnNode) insnNode, method);
                }
                //check if instruction type id of INVOKE_DYNAMIC type
                else if (insnNode.getType() == AbstractInsnNode.INVOKE_DYNAMIC_INSN) {
                    visitInvokeDynamicInsn((InvokeDynamicInsnNode) insnNode, method);
                }
            }
        }
    }
}

FindLinkedMethod is then recursively called to identify methods called inside a used method until all the used methods are identified.

License

Distributed under the Apache 2.0 license. See License for more information.

bytecodeoptimizer's People

Contributors

anjaleeps avatar

Stargazers

 avatar  avatar  avatar

Watchers

 avatar

Forkers

warunalakshitha

bytecodeoptimizer's Issues

build failed

$ gradle -q builder --stacktrace

FAILURE: Build failed with an exception.

  • What went wrong:
    Could not create service of type ScriptPluginFactory using BuildScopeServices.createScriptPluginFactory().

Could not create service of type PluginResolutionStrategyInternal using BuildScopeServices.createPluginResolutionStrategy().

  • Try:
    Run with --info or --debug option to get more log output. Run with --scan to get full insights.

  • Exception is:
    org.gradle.internal.service.ServiceCreationException: Could not create service of type ScriptPluginFactory using BuildScopeServices.createScriptPluginFactory().
    at org.gradle.internal.service.DefaultServiceRegistry$FactoryMethodService.invokeMethod(DefaultServiceRegistry.java:797)
    at org.gradle.internal.service.DefaultServiceRegistry$FactoryService.create(DefaultServiceRegistry.java:748)
    at org.gradle.internal.service.DefaultServiceRegistry$ManagedObjectProvider.getInstance(DefaultServiceRegistry.java:574)
    at org.gradle.internal.service.DefaultServiceRegistry$SingletonService.get(DefaultServiceRegistry.java:623)
    at org.gradle.internal.service.DefaultServiceRegistry$FactoryService.assembleParameters(DefaultServiceRegistry.java:761)
    at org.gradle.internal.service.DefaultServiceRegistry$FactoryService.create(DefaultServiceRegistry.java:747)
    at org.gradle.internal.service.DefaultServiceRegistry$ManagedObjectProvider.getInstance(DefaultServiceRegistry.java:574)
    at org.gradle.internal.service.DefaultServiceRegistry$SingletonService.get(DefaultServiceRegistry.java:623)
    at org.gradle.internal.service.DefaultServiceRegistry$FactoryService.assembleParameters(DefaultServiceRegistry.java:761)
    at org.gradle.internal.service.DefaultServiceRegistry$FactoryService.create(DefaultServiceRegistry.java:747)
    at org.gradle.internal.service.DefaultServiceRegistry$ManagedObjectProvider.getInstance(DefaultServiceRegistry.java:574)
    at org.gradle.internal.service.DefaultServiceRegistry$SingletonService.get(DefaultServiceRegistry.java:623)
    at org.gradle.internal.service.DefaultServiceRegistry.doGet(DefaultServiceRegistry.java:344)
    at org.gradle.internal.service.DefaultServiceRegistry.get(DefaultServiceRegistry.java:325)
    at org.gradle.initialization.DefaultGradleLauncherFactory.doNewInstance(DefaultGradleLauncherFactory.java:174)
    at org.gradle.initialization.DefaultGradleLauncherFactory.newInstance(DefaultGradleLauncherFactory.java:106)
    at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:40)
    at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:30)
    at org.gradle.launcher.exec.BuildTreeScopeBuildActionExecuter.execute(BuildTreeScopeBuildActionExecuter.java:39)
    at org.gradle.launcher.exec.BuildTreeScopeBuildActionExecuter.execute(BuildTreeScopeBuildActionExecuter.java:25)
    at org.gradle.tooling.internal.provider.ContinuousBuildActionExecuter.execute(ContinuousBuildActionExecuter.java:80)
    at org.gradle.tooling.internal.provider.ContinuousBuildActionExecuter.execute(ContinuousBuildActionExecuter.java:53)
    at org.gradle.tooling.internal.provider.ServicesSetupBuildActionExecuter.execute(ServicesSetupBuildActionExecuter.java:57)
    at org.gradle.tooling.internal.provider.ServicesSetupBuildActionExecuter.execute(ServicesSetupBuildActionExecuter.java:32)
    at org.gradle.tooling.internal.provider.GradleThreadBuildActionExecuter.execute(GradleThreadBuildActionExecuter.java:36)
    at org.gradle.tooling.internal.provider.GradleThreadBuildActionExecuter.execute(GradleThreadBuildActionExecuter.java:25)
    at org.gradle.tooling.internal.provider.ParallelismConfigurationBuildActionExecuter.execute(ParallelismConfigurationBuildActionExecuter.java:43)
    at org.gradle.tooling.internal.provider.ParallelismConfigurationBuildActionExecuter.execute(ParallelismConfigurationBuildActionExecuter.java:29)
    at org.gradle.tooling.internal.provider.StartParamsValidatingActionExecuter.execute(StartParamsValidatingActionExecuter.java:69)
    at org.gradle.tooling.internal.provider.StartParamsValidatingActionExecuter.execute(StartParamsValidatingActionExecuter.java:30)
    at org.gradle.tooling.internal.provider.SessionFailureReportingActionExecuter.execute(SessionFailureReportingActionExecuter.java:59)
    at org.gradle.tooling.internal.provider.SessionFailureReportingActionExecuter.execute(SessionFailureReportingActionExecuter.java:44)
    at org.gradle.tooling.internal.provider.SetupLoggingActionExecuter.execute(SetupLoggingActionExecuter.java:45)
    at org.gradle.tooling.internal.provider.SetupLoggingActionExecuter.execute(SetupLoggingActionExecuter.java:30)
    at org.gradle.launcher.daemon.server.exec.ExecuteBuild.doBuild(ExecuteBuild.java:67)
    at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:36)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:122)
    at org.gradle.launcher.daemon.server.exec.WatchForDisconnection.execute(WatchForDisconnection.java:37)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:122)
    at org.gradle.launcher.daemon.server.exec.ResetDeprecationLogger.execute(ResetDeprecationLogger.java:26)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:122)
    at org.gradle.launcher.daemon.server.exec.RequestStopIfSingleUsedDaemon.execute(RequestStopIfSingleUsedDaemon.java:34)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:122)
    at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.call(ForwardClientInput.java:74)
    at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.call(ForwardClientInput.java:72)
    at org.gradle.util.Swapper.swap(Swapper.java:38)
    at org.gradle.launcher.daemon.server.exec.ForwardClientInput.execute(ForwardClientInput.java:72)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:122)
    at org.gradle.launcher.daemon.server.exec.LogAndCheckHealth.execute(LogAndCheckHealth.java:55)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:122)
    at org.gradle.launcher.daemon.server.exec.LogToClient.doBuild(LogToClient.java:62)
    at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:36)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:122)
    at org.gradle.launcher.daemon.server.exec.EstablishBuildEnvironment.doBuild(EstablishBuildEnvironment.java:82)
    at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:36)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:122)
    at org.gradle.launcher.daemon.server.exec.StartBuildOrRespondWithBusy$1.run(StartBuildOrRespondWithBusy.java:50)
    at org.gradle.launcher.daemon.server.DaemonStateCoordinator$1.run(DaemonStateCoordinator.java:295)
    at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:63)
    at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:46)
    at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:55)
    Caused by: org.gradle.internal.service.ServiceCreationException: Could not create service of type PluginResolutionStrategyInternal using BuildScopeServices.createPluginResolutionStrategy().
    at org.gradle.internal.service.DefaultServiceRegistry$FactoryMethodService.invokeMethod(DefaultServiceRegistry.java:797)
    at org.gradle.internal.service.DefaultServiceRegistry$FactoryService.create(DefaultServiceRegistry.java:748)
    at org.gradle.internal.service.DefaultServiceRegistry$ManagedObjectProvider.getInstance(DefaultServiceRegistry.java:574)
    at org.gradle.internal.service.DefaultServiceRegistry$SingletonService.get(DefaultServiceRegistry.java:623)
    at org.gradle.internal.service.DefaultServiceRegistry$FactoryService.assembleParameters(DefaultServiceRegistry.java:761)
    at org.gradle.internal.service.DefaultServiceRegistry$FactoryService.create(DefaultServiceRegistry.java:747)
    at org.gradle.internal.service.DefaultServiceRegistry$ManagedObjectProvider.getInstance(DefaultServiceRegistry.java:574)
    at org.gradle.internal.service.DefaultServiceRegistry$SingletonService.get(DefaultServiceRegistry.java:623)
    at org.gradle.internal.service.DefaultServiceRegistry.doGet(DefaultServiceRegistry.java:344)
    at org.gradle.internal.service.DefaultServiceRegistry.get(DefaultServiceRegistry.java:325)
    at org.gradle.internal.service.scopes.BuildScopeServices.defaultScriptPluginFactory(BuildScopeServices.java:300)
    at org.gradle.internal.service.scopes.BuildScopeServices.createScriptPluginFactory(BuildScopeServices.java:287)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:73)
    at org.gradle.internal.service.ReflectionBasedServiceMethod.invoke(ReflectionBasedServiceMethod.java:35)
    at org.gradle.internal.service.DefaultServiceRegistry$FactoryMethodService.invokeMethod(DefaultServiceRegistry.java:795)
    ... 60 more
    Caused by: org.gradle.api.GradleException: Could not generate a proxy class for class org.gradle.plugin.management.internal.DefaultPluginResolutionStrategy.
    at org.gradle.api.internal.AbstractClassGenerator.generateUnderLock(AbstractClassGenerator.java:223)
    at org.gradle.api.internal.AbstractClassGenerator.generate(AbstractClassGenerator.java:79)
    at org.gradle.api.internal.ClassGeneratorBackedInstantiator.newInstance(ClassGeneratorBackedInstantiator.java:36)
    at org.gradle.plugin.internal.PluginUsePluginServiceRegistry$BuildScopeServices.createPluginResolutionStrategy(PluginUsePluginServiceRegistry.java:113)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:73)
    at org.gradle.internal.service.ReflectionBasedServiceMethod.invoke(ReflectionBasedServiceMethod.java:35)
    at org.gradle.internal.service.DefaultServiceRegistry$FactoryMethodService.invokeMethod(DefaultServiceRegistry.java:795)
    ... 77 more
    Caused by: org.gradle.internal.UncheckedException: java.lang.IllegalAccessException: module java.base does not open java.lang to unnamed module @7ea9e1e2
    at org.gradle.internal.UncheckedException.throwAsUncheckedException(UncheckedException.java:63)
    at org.gradle.internal.UncheckedException.throwAsUncheckedException(UncheckedException.java:40)
    at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:76)
    at org.gradle.internal.classloader.ClassLoaderUtils$LookupClassDefiner.defineClass(ClassLoaderUtils.java:142)
    at org.gradle.internal.classloader.ClassLoaderUtils.define(ClassLoaderUtils.java:92)
    at org.gradle.model.internal.asm.AsmClassGenerator.define(AsmClassGenerator.java:58)
    at org.gradle.model.internal.asm.AsmClassGenerator.define(AsmClassGenerator.java:54)
    at org.gradle.api.internal.AsmBackedClassGenerator$ClassBuilderImpl.generate(AsmBackedClassGenerator.java:967)
    at org.gradle.api.internal.AbstractClassGenerator.generateUnderLock(AbstractClassGenerator.java:221)
    ... 86 more
    Caused by: java.lang.IllegalAccessException: module java.base does not open java.lang to unnamed module @7ea9e1e2
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:73)
    ... 92 more

  • Get more help at https://help.gradle.org

BUILD FAILED in 1s

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.