AEM Sling Model Field Injection vs Constructor Injection Memory Consumption


Article was last updated on June 7th, 2020.

Sling Models field injectors are used to support injection of AEM Library-specific context objects. For example, @ScriptVariable SightlyWCMMode will inject the WCMMode object, @ScriptVariable Resource will inject the current resource object, and @ScriptVariable Style will inject the Style object. These objects are typically stored within the object, so it can be later used to construct the Sling Model’s properties to make available to the context who’s calling it.

With the Apache Sling Model’s injector specific annotations, we are able to inject Sling Objects, AEM Services, OSGI Components, etc… directly into the context of the Sling Model easily without much hassle.

What is the catch? Whenever we are injecting objects into our Sling Models via field injection for later use, an instantiated Sling Model adaptable stores a reference of those object in memory; stored injected objects in the Sling Model instance will consume memory.

The Sling Model constructor injection is also supported (as of Sling Models 1.1.0), and its documented that it does not store the reference to the adaptable, lets test this out.

In this article, we will test the Sling Model memory consumption with two scenarios:

  1. Test 1: Sling Model Field Injection
  2. Test 2: Sling Model Constructor Injection
  3. Conclusion

Test 1: Sling Model Field Injectors

I have 3 injected AEM objects to be stored in my class variables into my Sling Model via field injection; another 3 variables used to expose data to the calling context. Each AEM objects will then be used in the @PostConstruct method, where data would be extracted from each object, and set in the class variables for exposure to the calling context.

I then ran a test for getting the bytes in size for a specific object using the Lucene’s until RamUsageEstimator.

The memory byte size of the Sling Model adaptable object is 40.

MyModel.class : RamUsageEstimator Results:

1
2
3
import org.apache.lucene.util.RamUsageEstimator;
...
System.out.println("MyModel.class + shallowSizeOf:" + org.apache.lucene.util.RamUsageEstimator.shallowSizeOf(req.adaptTo(MyModel.class)));

MyModel.class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package com.sourcedcode.core.models;

import lombok.Getter;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.models.annotations.DefaultInjectionStrategy;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.injectorspecific.ScriptVariable;
import org.apache.sling.models.annotations.injectorspecific.SlingObject;

import javax.annotation.PostConstruct;

@Model(adaptables = {SlingHttpServletRequest.class,
        Resource.class}, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
public class MyModel {

    @ScriptVariable
    private Resource resource;

    @ScriptVariable
    private com.day.cq.wcm.api.Page currentPage;

    @SlingObject
    private SlingHttpServletRequest slingHttpServletRequest;

    @Getter
    private String currentResourcePath;

    @Getter
    private String currentPagePagePath;

    @Getter
    private String requestParam;


    @PostConstruct
    protected void initModel() {
        currentResourcePath = resource.getPath();
        currentPagePagePath = currentPage.getPath();
        requestParam = slingHttpServletRequest.getParameter("myParam");
    }
}

Test 2: Sling Model Constructor Injection

I have 3 injected AEM objects via Constructor Injection; another 3 variables used to expose data to the calling context. This time, I am not storing the AEM objects in as class variables. We can definitely see a change in byes size here.

The memory byte size of the Sling Model adaptable object is 24.

MyModelConstructor.class : RamUsageEstimator Results:

1
2
3
import org.apache.lucene.util.RamUsageEstimator;
...
System.out.println("MyModelConstructor.class + shallowSizeOf:" + org.apache.lucene.util.RamUsageEstimator.shallowSizeOf(req.adaptTo(MyModelConstructor.class)));

MyModelConstructor.class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package com.sourcedcode.core.models;

import lombok.Getter;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.models.annotations.DefaultInjectionStrategy;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.injectorspecific.ScriptVariable;
import org.apache.sling.models.annotations.injectorspecific.SlingObject;

import javax.inject.Inject;
import javax.inject.Named;

@Model(adaptables = {SlingHttpServletRequest.class, Resource.class}, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
public class MyModelConstructor {

    @Getter
    private String currentResourcePath;

    @Getter
    private String currentPagePagePath;

    @Getter
    private String requestParam;

    @Inject
    public MyModelConstructor(
            @ScriptVariable @Named("currentPage") final com.day.cq.wcm.api.Page currentPage,
            @ScriptVariable @Named("resource") final Resource resource,
            @SlingObject @Named("slingHttpServletRequest") final SlingHttpServletRequest slingHttpServletRequest
    ) {
        currentResourcePath = resource.getPath();
        currentPagePagePath = currentPage.getPath();
        requestParam = slingHttpServletRequest.getParameter("myParam");
    }
}

Conclusion

An AEM object that has been field injected into a variable that is only being minimally used holds onto memory which will/can be be wasting resources. If AEM objects are not needed, then the Constructor Injection method can be appropriately used.

It’s not rocket science. We see the results! Test 2 has less instance variables than Test 1; of course Test 2 will have a smaller Object (in byte size) than Test 1.

My only recommendation is this. When you are using AEM Objects, ask yourself, how is this Object being used?

This article highlights why Sling Model Constructor Injection via constructor is useful in our AEM development.


Looking for Help? Or need a mentor?

If you need help to super drive your AEM career road map, you can hire me for a one-hour session on codementor.io; We can work together on a price works just for you. https://www.codementor.io/@briankasingli. I'd be happy to help you along your way to becoming a high-quality AEM full-stack engineer.

Was this post helpful?

Hello, I am an enthusiastic Adobe Community Advisor and a seasoned Lead AEM Developer. I am currently serving as an AEM Technical Lead at MNPDigital.ca, bringing over a decade of extensive web engineering experience and more than eight years of practical AEM experience to the table. My goal is to give back to the AEM Full Stack Development community by sharing my wealth of knowledge with others. You can connect with me on LinkedIn.

One thought on “AEM Sling Model Field Injection vs Constructor Injection Memory Consumption

Leave a Reply

Your email address will not be published. Required fields are marked *

Back To Top