You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by Romain Manni-Bucau <rm...@gmail.com> on 2020/12/27 15:15:50 UTC

Tomcat 10 startup time and JMX as an opt in fature?

Hi everyone,

wonder if there is some work planned in tomcat 10 to 1. makes it start
faster and 2. make jmx optional.

I did some tests and locally a tomcat start is about 400ms.
I was surprised to see that Registry.disableRegistry() was taking already
65ms whereas it should be almost nothing so refactored the code, added a
RegistryFactory and really noop impl (not even a mock) and went down to
1ms, way better right? except the remaining 64ms went to the next line (new
StandardHost()). After some investigation time is mainly classloading time
(in all senses).

So I wonder if it wouldnt make sense to optimize tomcat startup time +
finally make JMX optional since it is no more used by users in most cases -
gues we must keep it as an optional plugin since it is used "historically".

The proposal would be to:

1. reduce classloading tree as much as possible for default case - here
having jmx optional will help a lot
2. probably generate resource bundle as .java at build time to avoid all
the classloading resource bundle implies (it is like 74 loadClass + as much
getResource for a simple startup and all loadclass are misses)
2.bis. probably add a mode where StringManager uses the default bundle
without passing through the resource bundle layer
3. cut dead code when we know it is inactive (typically the case for jmx)
4. open point: bypassing Context: in "main" mode (as the code shared
after), you just want to bind some logic in a servlet/filter/valve, you
don't always care about handling contexts (or a single one) so wonder if we
shouldnt evaluate the fact to add the user logic in a valve for this kind
of bench and maybe document this case (spring boot could inherit from it
since it almost never use anything else that default spring servlet and
binds everything in it).

Here is the kind of case I'd like to see insanely fast (<100ms - and it is
possible regarding what it does):

public class Main {
    public static void main(String[] args) throws LifecycleException,
InterruptedException {
        final long start = System.nanoTime();
        Registry.disableRegistry();
        final long startDisableRegistry = System.nanoTime();

        final Host host = new StandardHost();
        host.setName("localhost");
        final long startHost = System.nanoTime();

        final Engine engine = new StandardEngine();
        engine.setName("Tomcat");
        engine.setDefaultHost("localhost");
        engine.setRealm(new NullRealm());
        engine.addChild(host);
        final long startEngine = System.nanoTime();

        final var protocolHandler = new Http11Nio2Protocol();
        protocolHandler.setPort(8080);
        final long startHttp11Nio2Protocol = System.nanoTime();

        final Service service = new StandardService();
        service.setName("Tomcat");
        service.setContainer(engine);
        service.addConnector(new Connector(protocolHandler));
        final long startService = System.nanoTime();

        final StandardServer server = new StandardServer();
        server.setPort(-1);
        server.addService(service);
        final long startServer = System.nanoTime();

        final StandardContext context = new StandardContext();
        context.setUseNaming(false);
        context.setClearReferencesStopThreads(false);
        context.setClearReferencesStopTimerThreads(false);
        context.setClearReferencesHttpClientKeepAliveThread(false);
        context.setClearReferencesRmiTargets(false);
        context.setClearReferencesThreadLocals(false);
        context.setClearReferencesObjectStreamClassCaches(false);
        context.setWorkDir(System.getProperty("java.io.tmpdir"));
        context.setName("");
        context.setPath("");
        context.setDocBase(null);
        context.addLifecycleListener(event -> {
            if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {
                context.setConfigured(true);
                if (context.getLoginConfig() == null) {
                    context.setLoginConfig(new LoginConfig("NONE", null,
null, null));
                    context.getPipeline().addValve(new
NonLoginAuthenticator());
                }
            }
        });
        context.addServletContainerInitializer((classes, ctx) -> {
            ctx.addServlet("default", new DefaultServlet())
                    .addMapping("/");
        }, Set.of());
        final long startContext = System.nanoTime();

        host.addChild(context);

        server.init();
        final long inited = System.nanoTime();
        server.start();
        final long started = System.nanoTime();
        // new CountDownLatch(1).await();

        final var contextClassLoader =
Thread.currentThread().getContextClassLoader();

        server.stop();
        server.destroy();
        final long stopped = System.nanoTime();

        System.out.println(TimeUnit.NANOSECONDS.toMillis(started - start) +
"ms");
        System.out.println(TimeUnit.NANOSECONDS.toMillis(stopped - started)
+ "ms");
        System.out.println();
        System.out.println("startDisableRegistry:    " +
TimeUnit.NANOSECONDS.toMillis(startDisableRegistry - start) + "ms");
        System.out.println("startHost:               " +
TimeUnit.NANOSECONDS.toMillis(startHost - startDisableRegistry) + "ms");
        System.out.println("startEngine:             " +
TimeUnit.NANOSECONDS.toMillis(startEngine - startHost) + "ms");
        System.out.println("startHttp11Nio2Protocol: " +
TimeUnit.NANOSECONDS.toMillis(startHttp11Nio2Protocol - startEngine) +
"ms");
        System.out.println("startService:            " +
TimeUnit.NANOSECONDS.toMillis(startService - startEngine) + "ms");
        System.out.println("startServer:             " +
TimeUnit.NANOSECONDS.toMillis(startServer - startService) + "ms");
        System.out.println("startContext:            " +
TimeUnit.NANOSECONDS.toMillis(startContext - startServer) + "ms");
        System.out.println("actualInit:              " +
TimeUnit.NANOSECONDS.toMillis(inited - startContext) + "ms");
        System.out.println("actualStart:             " +
TimeUnit.NANOSECONDS.toMillis(started - inited) + "ms");
    }
}


Current reference output (medium values) is - on java 11:

364ms
22ms

startDisableRegistry:    58ms
startHost:               19ms
startEngine:             7ms
startHttp11Nio2Protocol: 25ms
startService:            39ms
startServer:             12ms
startContext:            26ms
actualStart:             200ms

Wdyt? Anyone had a look before?


PS: if curious the registry patch i did was:
https://gist.github.com/rmannibucau/e7cb9674fa4a21ccfe9eb956b509fad1 -
indeed since it does not help overall I don't ask to integrate it at all,
just a FYI

Romain Manni-Bucau
@rmannibucau <https://twitter.com/rmannibucau> |  Blog
<https://rmannibucau.metawerx.net/> | Old Blog
<http://rmannibucau.wordpress.com> | Github <https://github.com/rmannibucau> |
LinkedIn <https://www.linkedin.com/in/rmannibucau> | Book
<https://www.packtpub.com/application-development/java-ee-8-high-performance>

Re: Tomcat 10 startup time and JMX as an opt in fature?

Posted by Jean-Baptiste Onofre <jb...@nanthrax.net>.
Hi,

If it’s optional but still enabled, I think it’s a good idea to speedup bootstrapping.

Just my $0.01 ;)

Regards
JB

> Le 27 déc. 2020 à 16:15, Romain Manni-Bucau <rm...@gmail.com> a écrit :
> 
> Hi everyone,
> 
> wonder if there is some work planned in tomcat 10 to 1. makes it start
> faster and 2. make jmx optional.
> 
> I did some tests and locally a tomcat start is about 400ms.
> I was surprised to see that Registry.disableRegistry() was taking already
> 65ms whereas it should be almost nothing so refactored the code, added a
> RegistryFactory and really noop impl (not even a mock) and went down to
> 1ms, way better right? except the remaining 64ms went to the next line (new
> StandardHost()). After some investigation time is mainly classloading time
> (in all senses).
> 
> So I wonder if it wouldnt make sense to optimize tomcat startup time +
> finally make JMX optional since it is no more used by users in most cases -
> gues we must keep it as an optional plugin since it is used "historically".
> 
> The proposal would be to:
> 
> 1. reduce classloading tree as much as possible for default case - here
> having jmx optional will help a lot
> 2. probably generate resource bundle as .java at build time to avoid all
> the classloading resource bundle implies (it is like 74 loadClass + as much
> getResource for a simple startup and all loadclass are misses)
> 2.bis. probably add a mode where StringManager uses the default bundle
> without passing through the resource bundle layer
> 3. cut dead code when we know it is inactive (typically the case for jmx)
> 4. open point: bypassing Context: in "main" mode (as the code shared
> after), you just want to bind some logic in a servlet/filter/valve, you
> don't always care about handling contexts (or a single one) so wonder if we
> shouldnt evaluate the fact to add the user logic in a valve for this kind
> of bench and maybe document this case (spring boot could inherit from it
> since it almost never use anything else that default spring servlet and
> binds everything in it).
> 
> Here is the kind of case I'd like to see insanely fast (<100ms - and it is
> possible regarding what it does):
> 
> public class Main {
>    public static void main(String[] args) throws LifecycleException,
> InterruptedException {
>        final long start = System.nanoTime();
>        Registry.disableRegistry();
>        final long startDisableRegistry = System.nanoTime();
> 
>        final Host host = new StandardHost();
>        host.setName("localhost");
>        final long startHost = System.nanoTime();
> 
>        final Engine engine = new StandardEngine();
>        engine.setName("Tomcat");
>        engine.setDefaultHost("localhost");
>        engine.setRealm(new NullRealm());
>        engine.addChild(host);
>        final long startEngine = System.nanoTime();
> 
>        final var protocolHandler = new Http11Nio2Protocol();
>        protocolHandler.setPort(8080);
>        final long startHttp11Nio2Protocol = System.nanoTime();
> 
>        final Service service = new StandardService();
>        service.setName("Tomcat");
>        service.setContainer(engine);
>        service.addConnector(new Connector(protocolHandler));
>        final long startService = System.nanoTime();
> 
>        final StandardServer server = new StandardServer();
>        server.setPort(-1);
>        server.addService(service);
>        final long startServer = System.nanoTime();
> 
>        final StandardContext context = new StandardContext();
>        context.setUseNaming(false);
>        context.setClearReferencesStopThreads(false);
>        context.setClearReferencesStopTimerThreads(false);
>        context.setClearReferencesHttpClientKeepAliveThread(false);
>        context.setClearReferencesRmiTargets(false);
>        context.setClearReferencesThreadLocals(false);
>        context.setClearReferencesObjectStreamClassCaches(false);
>        context.setWorkDir(System.getProperty("java.io.tmpdir"));
>        context.setName("");
>        context.setPath("");
>        context.setDocBase(null);
>        context.addLifecycleListener(event -> {
>            if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {
>                context.setConfigured(true);
>                if (context.getLoginConfig() == null) {
>                    context.setLoginConfig(new LoginConfig("NONE", null,
> null, null));
>                    context.getPipeline().addValve(new
> NonLoginAuthenticator());
>                }
>            }
>        });
>        context.addServletContainerInitializer((classes, ctx) -> {
>            ctx.addServlet("default", new DefaultServlet())
>                    .addMapping("/");
>        }, Set.of());
>        final long startContext = System.nanoTime();
> 
>        host.addChild(context);
> 
>        server.init();
>        final long inited = System.nanoTime();
>        server.start();
>        final long started = System.nanoTime();
>        // new CountDownLatch(1).await();
> 
>        final var contextClassLoader =
> Thread.currentThread().getContextClassLoader();
> 
>        server.stop();
>        server.destroy();
>        final long stopped = System.nanoTime();
> 
>        System.out.println(TimeUnit.NANOSECONDS.toMillis(started - start) +
> "ms");
>        System.out.println(TimeUnit.NANOSECONDS.toMillis(stopped - started)
> + "ms");
>        System.out.println();
>        System.out.println("startDisableRegistry:    " +
> TimeUnit.NANOSECONDS.toMillis(startDisableRegistry - start) + "ms");
>        System.out.println("startHost:               " +
> TimeUnit.NANOSECONDS.toMillis(startHost - startDisableRegistry) + "ms");
>        System.out.println("startEngine:             " +
> TimeUnit.NANOSECONDS.toMillis(startEngine - startHost) + "ms");
>        System.out.println("startHttp11Nio2Protocol: " +
> TimeUnit.NANOSECONDS.toMillis(startHttp11Nio2Protocol - startEngine) +
> "ms");
>        System.out.println("startService:            " +
> TimeUnit.NANOSECONDS.toMillis(startService - startEngine) + "ms");
>        System.out.println("startServer:             " +
> TimeUnit.NANOSECONDS.toMillis(startServer - startService) + "ms");
>        System.out.println("startContext:            " +
> TimeUnit.NANOSECONDS.toMillis(startContext - startServer) + "ms");
>        System.out.println("actualInit:              " +
> TimeUnit.NANOSECONDS.toMillis(inited - startContext) + "ms");
>        System.out.println("actualStart:             " +
> TimeUnit.NANOSECONDS.toMillis(started - inited) + "ms");
>    }
> }
> 
> 
> Current reference output (medium values) is - on java 11:
> 
> 364ms
> 22ms
> 
> startDisableRegistry:    58ms
> startHost:               19ms
> startEngine:             7ms
> startHttp11Nio2Protocol: 25ms
> startService:            39ms
> startServer:             12ms
> startContext:            26ms
> actualStart:             200ms
> 
> Wdyt? Anyone had a look before?
> 
> 
> PS: if curious the registry patch i did was:
> https://gist.github.com/rmannibucau/e7cb9674fa4a21ccfe9eb956b509fad1 -
> indeed since it does not help overall I don't ask to integrate it at all,
> just a FYI
> 
> Romain Manni-Bucau
> @rmannibucau <https://twitter.com/rmannibucau> |  Blog
> <https://rmannibucau.metawerx.net/> | Old Blog
> <http://rmannibucau.wordpress.com> | Github <https://github.com/rmannibucau> |
> LinkedIn <https://www.linkedin.com/in/rmannibucau> | Book
> <https://www.packtpub.com/application-development/java-ee-8-high-performance>


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


Re: Tomcat 10 startup time and JMX as an opt in fature?

Posted by Romain Manni-Bucau <rm...@gmail.com>.
Le lun. 28 déc. 2020 à 13:48, Rémy Maucherat <re...@apache.org> a écrit :

> On Sun, Dec 27, 2020 at 4:16 PM Romain Manni-Bucau <rm...@gmail.com>
> wrote:
>
> > Hi everyone,
> >
> > wonder if there is some work planned in tomcat 10 to 1. makes it start
> > faster and 2. make jmx optional.
> >
> > I did some tests and locally a tomcat start is about 400ms.
> > I was surprised to see that Registry.disableRegistry() was taking already
> > 65ms whereas it should be almost nothing so refactored the code, added a
> > RegistryFactory and really noop impl (not even a mock) and went down to
> > 1ms, way better right? except the remaining 64ms went to the next line
> (new
> > StandardHost()). After some investigation time is mainly classloading
> time
> > (in all senses).
> >
> > So I wonder if it wouldnt make sense to optimize tomcat startup time +
> > finally make JMX optional since it is no more used by users in most
> cases -
> > gues we must keep it as an optional plugin since it is used
> "historically".
> >
> > The proposal would be to:
> >
> > 1. reduce classloading tree as much as possible for default case - here
> > having jmx optional will help a lot
> > 2. probably generate resource bundle as .java at build time to avoid all
> > the classloading resource bundle implies (it is like 74 loadClass + as
> much
> > getResource for a simple startup and all loadclass are misses)
> > 2.bis. probably add a mode where StringManager uses the default bundle
> > without passing through the resource bundle layer
> > 3. cut dead code when we know it is inactive (typically the case for jmx)
> > 4. open point: bypassing Context: in "main" mode (as the code shared
> > after), you just want to bind some logic in a servlet/filter/valve, you
> > don't always care about handling contexts (or a single one) so wonder if
> we
> > shouldnt evaluate the fact to add the user logic in a valve for this kind
> > of bench and maybe document this case (spring boot could inherit from it
> > since it almost never use anything else that default spring servlet and
> > binds everything in it).
> >
> > Here is the kind of case I'd like to see insanely fast (<100ms - and it
> is
> > possible regarding what it does):
> >
> > public class Main {
> >     public static void main(String[] args) throws LifecycleException,
> > InterruptedException {
> >         final long start = System.nanoTime();
> >         Registry.disableRegistry();
> >         final long startDisableRegistry = System.nanoTime();
> >
> >         final Host host = new StandardHost();
> >         host.setName("localhost");
> >         final long startHost = System.nanoTime();
> >
> >         final Engine engine = new StandardEngine();
> >         engine.setName("Tomcat");
> >         engine.setDefaultHost("localhost");
> >         engine.setRealm(new NullRealm());
> >         engine.addChild(host);
> >         final long startEngine = System.nanoTime();
> >
> >         final var protocolHandler = new Http11Nio2Protocol();
> >         protocolHandler.setPort(8080);
> >         final long startHttp11Nio2Protocol = System.nanoTime();
> >
> >         final Service service = new StandardService();
> >         service.setName("Tomcat");
> >         service.setContainer(engine);
> >         service.addConnector(new Connector(protocolHandler));
> >         final long startService = System.nanoTime();
> >
> >         final StandardServer server = new StandardServer();
> >         server.setPort(-1);
> >         server.addService(service);
> >         final long startServer = System.nanoTime();
> >
> >         final StandardContext context = new StandardContext();
> >         context.setUseNaming(false);
> >         context.setClearReferencesStopThreads(false);
> >         context.setClearReferencesStopTimerThreads(false);
> >         context.setClearReferencesHttpClientKeepAliveThread(false);
> >         context.setClearReferencesRmiTargets(false);
> >         context.setClearReferencesThreadLocals(false);
> >         context.setClearReferencesObjectStreamClassCaches(false);
> >         context.setWorkDir(System.getProperty("java.io.tmpdir"));
> >         context.setName("");
> >         context.setPath("");
> >         context.setDocBase(null);
> >         context.addLifecycleListener(event -> {
> >             if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT))
> {
> >                 context.setConfigured(true);
> >                 if (context.getLoginConfig() == null) {
> >                     context.setLoginConfig(new LoginConfig("NONE", null,
> > null, null));
> >                     context.getPipeline().addValve(new
> > NonLoginAuthenticator());
> >                 }
> >             }
> >         });
> >         context.addServletContainerInitializer((classes, ctx) -> {
> >             ctx.addServlet("default", new DefaultServlet())
> >                     .addMapping("/");
> >         }, Set.of());
> >         final long startContext = System.nanoTime();
> >
> >         host.addChild(context);
> >
> >         server.init();
> >         final long inited = System.nanoTime();
> >         server.start();
> >         final long started = System.nanoTime();
> >         // new CountDownLatch(1).await();
> >
> >         final var contextClassLoader =
> > Thread.currentThread().getContextClassLoader();
> >
> >         server.stop();
> >         server.destroy();
> >         final long stopped = System.nanoTime();
> >
> >         System.out.println(TimeUnit.NANOSECONDS.toMillis(started -
> start) +
> > "ms");
> >         System.out.println(TimeUnit.NANOSECONDS.toMillis(stopped -
> started)
> > + "ms");
> >         System.out.println();
> >         System.out.println("startDisableRegistry:    " +
> > TimeUnit.NANOSECONDS.toMillis(startDisableRegistry - start) + "ms");
> >         System.out.println("startHost:               " +
> > TimeUnit.NANOSECONDS.toMillis(startHost - startDisableRegistry) + "ms");
> >         System.out.println("startEngine:             " +
> > TimeUnit.NANOSECONDS.toMillis(startEngine - startHost) + "ms");
> >         System.out.println("startHttp11Nio2Protocol: " +
> > TimeUnit.NANOSECONDS.toMillis(startHttp11Nio2Protocol - startEngine) +
> > "ms");
> >         System.out.println("startService:            " +
> > TimeUnit.NANOSECONDS.toMillis(startService - startEngine) + "ms");
> >         System.out.println("startServer:             " +
> > TimeUnit.NANOSECONDS.toMillis(startServer - startService) + "ms");
> >         System.out.println("startContext:            " +
> > TimeUnit.NANOSECONDS.toMillis(startContext - startServer) + "ms");
> >         System.out.println("actualInit:              " +
> > TimeUnit.NANOSECONDS.toMillis(inited - startContext) + "ms");
> >         System.out.println("actualStart:             " +
> > TimeUnit.NANOSECONDS.toMillis(started - inited) + "ms");
> >     }
> > }
> >
> >
> > Current reference output (medium values) is - on java 11:
> >
> > 364ms
> > 22ms
> >
> > startDisableRegistry:    58ms
> > startHost:               19ms
> > startEngine:             7ms
> > startHttp11Nio2Protocol: 25ms
> > startService:            39ms
> > startServer:             12ms
> > startContext:            26ms
> > actualStart:             200ms
> >
> > Wdyt? Anyone had a look before?
> >
>
> It is not possible to disable JMX by default since in addition to the
> obvious monitoring with visualvm or similar, it is also a building block
> for a lot of other features in Tomcat (manager and status webapps, TLS
> reload, storeconfig). It's a bit the same for JNDI.
>

Well technically it is. Let's step back and see how JMX is used in tomcat
today:

1. JMX is more or less the parent class of all components
-> this can trivially be replaced by composition pattern - reusing tomcat
listeners - which will solve it in a neat way
It is also why I mentionned it is almost no more used today:
a. monitoring -> prometheus, zipkin, grafana, graphite and so on often use
an exporter which is not JMX, JMX is even not recommended for prod so
generally people set up something else - at least I never saw it used in
prod, only in dev+benchmarks
b. tomcat features using it: aka manager, status, storeconfig, ssl reload:
for me it is ok to make these features conditional to JMX enablement (JMX
server listener is in server.xml, if not all these features don't work).
That said, it is here again mainly because it was coded using JMX but a
fallback could be to use the local server instance which is doable from a
webapp. Only missing piece would be to control a remote tomcat, which
assumes a remote JMX setup is done so guess it is more than fine to enforce
JMX for this rare case.

So overall I see it as a future friendly refactoring (this is why i didn't
only linked JMX part of this thread to perf but also JMX by itself in the
subject even if I went there from the perf perspectives).


>
> Overall, AOT compilation is likely a better solution for the use case you
> describe, that was the whole point of adding support for it.
>

Yes and no, AOT has a lot of troubles:

1. It does not always work
2. Its maintenance cost is just insane, in particular when you don't do a
hello world and add some lib
3. It is not always insanely faster and it is generally more unstable for
now (until we get better GC in community edition)
4. There is no free and up to date/complete version
5. Resulting license is not always desired
6. It breaks docker/OCI layer caching
7. It is just too slow to build
8. It requires to run twice tests (which adds to the slowness of 7)

CDS helps but technically there is no real reason to be that slow OOTB for
the main I shared so, since Tomcat is designed to be pluggable, I think it
would be worth making it a target to use that pluggability for 10.x (not
previous branches since it can break a few things maybe?).

So overall I think CDS is a good solution for fast startup and not AOT - as
of today - but the point is more about tomcat future and the opportunities
jakarta breaking change would enable (mainly let Tomcat embrace modern
practise to embed it in a light fashion).

Hope it makes sense.


>
> Rémy
>
>
> >
> >
> > PS: if curious the registry patch i did was:
> > https://gist.github.com/rmannibucau/e7cb9674fa4a21ccfe9eb956b509fad1 -
> > indeed since it does not help overall I don't ask to integrate it at all,
> > just a FYI
> >
> > Romain Manni-Bucau
> > @rmannibucau <https://twitter.com/rmannibucau> |  Blog
> > <https://rmannibucau.metawerx.net/> | Old Blog
> > <http://rmannibucau.wordpress.com> | Github <
> > https://github.com/rmannibucau> |
> > LinkedIn <https://www.linkedin.com/in/rmannibucau> | Book
> > <
> >
> https://www.packtpub.com/application-development/java-ee-8-high-performance
> > >
> >
>

Re: Tomcat 10 startup time and JMX as an opt in fature?

Posted by Rémy Maucherat <re...@apache.org>.
On Sun, Dec 27, 2020 at 4:16 PM Romain Manni-Bucau <rm...@gmail.com>
wrote:

> Hi everyone,
>
> wonder if there is some work planned in tomcat 10 to 1. makes it start
> faster and 2. make jmx optional.
>
> I did some tests and locally a tomcat start is about 400ms.
> I was surprised to see that Registry.disableRegistry() was taking already
> 65ms whereas it should be almost nothing so refactored the code, added a
> RegistryFactory and really noop impl (not even a mock) and went down to
> 1ms, way better right? except the remaining 64ms went to the next line (new
> StandardHost()). After some investigation time is mainly classloading time
> (in all senses).
>
> So I wonder if it wouldnt make sense to optimize tomcat startup time +
> finally make JMX optional since it is no more used by users in most cases -
> gues we must keep it as an optional plugin since it is used "historically".
>
> The proposal would be to:
>
> 1. reduce classloading tree as much as possible for default case - here
> having jmx optional will help a lot
> 2. probably generate resource bundle as .java at build time to avoid all
> the classloading resource bundle implies (it is like 74 loadClass + as much
> getResource for a simple startup and all loadclass are misses)
> 2.bis. probably add a mode where StringManager uses the default bundle
> without passing through the resource bundle layer
> 3. cut dead code when we know it is inactive (typically the case for jmx)
> 4. open point: bypassing Context: in "main" mode (as the code shared
> after), you just want to bind some logic in a servlet/filter/valve, you
> don't always care about handling contexts (or a single one) so wonder if we
> shouldnt evaluate the fact to add the user logic in a valve for this kind
> of bench and maybe document this case (spring boot could inherit from it
> since it almost never use anything else that default spring servlet and
> binds everything in it).
>
> Here is the kind of case I'd like to see insanely fast (<100ms - and it is
> possible regarding what it does):
>
> public class Main {
>     public static void main(String[] args) throws LifecycleException,
> InterruptedException {
>         final long start = System.nanoTime();
>         Registry.disableRegistry();
>         final long startDisableRegistry = System.nanoTime();
>
>         final Host host = new StandardHost();
>         host.setName("localhost");
>         final long startHost = System.nanoTime();
>
>         final Engine engine = new StandardEngine();
>         engine.setName("Tomcat");
>         engine.setDefaultHost("localhost");
>         engine.setRealm(new NullRealm());
>         engine.addChild(host);
>         final long startEngine = System.nanoTime();
>
>         final var protocolHandler = new Http11Nio2Protocol();
>         protocolHandler.setPort(8080);
>         final long startHttp11Nio2Protocol = System.nanoTime();
>
>         final Service service = new StandardService();
>         service.setName("Tomcat");
>         service.setContainer(engine);
>         service.addConnector(new Connector(protocolHandler));
>         final long startService = System.nanoTime();
>
>         final StandardServer server = new StandardServer();
>         server.setPort(-1);
>         server.addService(service);
>         final long startServer = System.nanoTime();
>
>         final StandardContext context = new StandardContext();
>         context.setUseNaming(false);
>         context.setClearReferencesStopThreads(false);
>         context.setClearReferencesStopTimerThreads(false);
>         context.setClearReferencesHttpClientKeepAliveThread(false);
>         context.setClearReferencesRmiTargets(false);
>         context.setClearReferencesThreadLocals(false);
>         context.setClearReferencesObjectStreamClassCaches(false);
>         context.setWorkDir(System.getProperty("java.io.tmpdir"));
>         context.setName("");
>         context.setPath("");
>         context.setDocBase(null);
>         context.addLifecycleListener(event -> {
>             if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {
>                 context.setConfigured(true);
>                 if (context.getLoginConfig() == null) {
>                     context.setLoginConfig(new LoginConfig("NONE", null,
> null, null));
>                     context.getPipeline().addValve(new
> NonLoginAuthenticator());
>                 }
>             }
>         });
>         context.addServletContainerInitializer((classes, ctx) -> {
>             ctx.addServlet("default", new DefaultServlet())
>                     .addMapping("/");
>         }, Set.of());
>         final long startContext = System.nanoTime();
>
>         host.addChild(context);
>
>         server.init();
>         final long inited = System.nanoTime();
>         server.start();
>         final long started = System.nanoTime();
>         // new CountDownLatch(1).await();
>
>         final var contextClassLoader =
> Thread.currentThread().getContextClassLoader();
>
>         server.stop();
>         server.destroy();
>         final long stopped = System.nanoTime();
>
>         System.out.println(TimeUnit.NANOSECONDS.toMillis(started - start) +
> "ms");
>         System.out.println(TimeUnit.NANOSECONDS.toMillis(stopped - started)
> + "ms");
>         System.out.println();
>         System.out.println("startDisableRegistry:    " +
> TimeUnit.NANOSECONDS.toMillis(startDisableRegistry - start) + "ms");
>         System.out.println("startHost:               " +
> TimeUnit.NANOSECONDS.toMillis(startHost - startDisableRegistry) + "ms");
>         System.out.println("startEngine:             " +
> TimeUnit.NANOSECONDS.toMillis(startEngine - startHost) + "ms");
>         System.out.println("startHttp11Nio2Protocol: " +
> TimeUnit.NANOSECONDS.toMillis(startHttp11Nio2Protocol - startEngine) +
> "ms");
>         System.out.println("startService:            " +
> TimeUnit.NANOSECONDS.toMillis(startService - startEngine) + "ms");
>         System.out.println("startServer:             " +
> TimeUnit.NANOSECONDS.toMillis(startServer - startService) + "ms");
>         System.out.println("startContext:            " +
> TimeUnit.NANOSECONDS.toMillis(startContext - startServer) + "ms");
>         System.out.println("actualInit:              " +
> TimeUnit.NANOSECONDS.toMillis(inited - startContext) + "ms");
>         System.out.println("actualStart:             " +
> TimeUnit.NANOSECONDS.toMillis(started - inited) + "ms");
>     }
> }
>
>
> Current reference output (medium values) is - on java 11:
>
> 364ms
> 22ms
>
> startDisableRegistry:    58ms
> startHost:               19ms
> startEngine:             7ms
> startHttp11Nio2Protocol: 25ms
> startService:            39ms
> startServer:             12ms
> startContext:            26ms
> actualStart:             200ms
>
> Wdyt? Anyone had a look before?
>

It is not possible to disable JMX by default since in addition to the
obvious monitoring with visualvm or similar, it is also a building block
for a lot of other features in Tomcat (manager and status webapps, TLS
reload, storeconfig). It's a bit the same for JNDI.

Overall, AOT compilation is likely a better solution for the use case you
describe, that was the whole point of adding support for it.

Rémy


>
>
> PS: if curious the registry patch i did was:
> https://gist.github.com/rmannibucau/e7cb9674fa4a21ccfe9eb956b509fad1 -
> indeed since it does not help overall I don't ask to integrate it at all,
> just a FYI
>
> Romain Manni-Bucau
> @rmannibucau <https://twitter.com/rmannibucau> |  Blog
> <https://rmannibucau.metawerx.net/> | Old Blog
> <http://rmannibucau.wordpress.com> | Github <
> https://github.com/rmannibucau> |
> LinkedIn <https://www.linkedin.com/in/rmannibucau> | Book
> <
> https://www.packtpub.com/application-development/java-ee-8-high-performance
> >
>