javagradleshadowjar

Why shadowJar minimise() is not removing all unused dependencies?


I'm trying to generate two different jars from a project which is a monorepo that includes dependencies to many many great things in this world, so needless to say i do not want these executable jars to have big sizes and would rather have them include only classes that they actually use.

I followed instructions here https://imperceptiblethoughts.com/shadow/configuration/minimizing/.

In order to test if shadowJar task actually works i created a simple Main java file that practically uses nothing other than java SDK.

public class Main {
    public static void main(String[] args) {
        System.out.println("client");
    }
}

and added the actual shadow jar task and pointed to this as a main class

shadowJar {
    archiveBaseName.set('client')
    archiveClassifier.set('')
    archiveVersion.set('0.1')
    minimize()

    manifest {
        attributes 'Main-Class': 'my.package.Main'
    }
}

which generates the uber jar correctly (i was able to run it simply with java -jar ...), however the size of the file is 10Mb.

then i generated a new version without minimize() part and it was 15Mb.

Is there something i do wrong here? Maybe i'm expecting too much from this tool and proguard (as tedious as it is to setup) is the only way?


Solution

  • Since you did not provide us with your build file I can only give some general advice:

    As you have stated yourself, the minimizing works. I also did some tests myself (see below) with many dependencies. The jar file was at 187MB without minimizing and had so many entries I needed to enable zip64. Shadow minimized it to just 25MB.

    Now, where do those 25MB come from? From what I have seen in my testing there are two types of files that stay:

    Those can not be stripped automatically since it is unclear whether they are used or not.

    If you are 100% sure you do not need some of those files you could filter them out from your jar manually. Although I doubt that it is worth the effort.

    If you think there are other files included as well, you could extract the Jar (a jar is just a zip with a different name extension) and look at its content.

    Test Case

    First off, I cloned Spring Boot, then I removed all unnecessary modules (that is everything except spring-boot-project:spring-boot, spring-boot-project:spring-boot-dependencies and spring-boot-project:spring-boot-parent) and changed all dependencies of spring-boot-project:spring-boot to be of type implementation. Then I removed the current source code and replaced it with your sample Main class. After adding the shadow plugin I got a jar with a size of 187MB. When I then applied minimize it was only 25MB.

    I then extracted the Jar and ran some commands to remove every package-info, module-info and general resource file:

    $> find \( \( -not -name "*.class" -or -name "package-info.class" -or -name "module-info.class" \) -and -not -type d \) -delete
    $> find -empty -delete
    

    I do not know if there is an equivalent command on Windows. In any case, doing the above leaves only a few number of class files left (144kB!), so I think it is save to say that shadow is doing its job.