Partner – Microsoft – NPI EA (cat = Baeldung)
announcement - icon

Azure Container Apps is a fully managed serverless container service that enables you to build and deploy modern, cloud-native Java applications and microservices at scale. It offers a simplified developer experience while providing the flexibility and portability of containers.

Of course, Azure Container Apps has really solid support for our ecosystem, from a number of build options, managed Java components, native metrics, dynamic logger, and quite a bit more.

To learn more about Java features on Azure Container Apps, visit the documentation page.

You can also ask questions and leave feedback on the Azure Container Apps GitHub page.

Partner – Microsoft – NPI EA (cat= Spring Boot)
announcement - icon

Azure Container Apps is a fully managed serverless container service that enables you to build and deploy modern, cloud-native Java applications and microservices at scale. It offers a simplified developer experience while providing the flexibility and portability of containers.

Of course, Azure Container Apps has really solid support for our ecosystem, from a number of build options, managed Java components, native metrics, dynamic logger, and quite a bit more.

To learn more about Java features on Azure Container Apps, you can get started over on the documentation page.

And, you can also ask questions and leave feedback on the Azure Container Apps GitHub page.

Partner – Orkes – NPI EA (cat=Spring)
announcement - icon

Modern software architecture is often broken. Slow delivery leads to missed opportunities, innovation is stalled due to architectural complexities, and engineering resources are exceedingly expensive.

Orkes is the leading workflow orchestration platform built to enable teams to transform the way they develop, connect, and deploy applications, microservices, AI agents, and more.

With Orkes Conductor managed through Orkes Cloud, developers can focus on building mission critical applications without worrying about infrastructure maintenance to meet goals and, simply put, taking new products live faster and reducing total cost of ownership.

Try a 14-Day Free Trial of Orkes Conductor today.

Partner – Orkes – NPI EA (tag=Microservices)
announcement - icon

Modern software architecture is often broken. Slow delivery leads to missed opportunities, innovation is stalled due to architectural complexities, and engineering resources are exceedingly expensive.

Orkes is the leading workflow orchestration platform built to enable teams to transform the way they develop, connect, and deploy applications, microservices, AI agents, and more.

With Orkes Conductor managed through Orkes Cloud, developers can focus on building mission critical applications without worrying about infrastructure maintenance to meet goals and, simply put, taking new products live faster and reducing total cost of ownership.

Try a 14-Day Free Trial of Orkes Conductor today.

eBook – Guide Spring Cloud – NPI EA (cat=Spring Cloud)
announcement - icon

Let's get started with a Microservice Architecture with Spring Cloud:

>> Join Pro and download the eBook

eBook – Mockito – NPI EA (tag = Mockito)
announcement - icon

Mocking is an essential part of unit testing, and the Mockito library makes it easy to write clean and intuitive unit tests for your Java code.

Get started with mocking and improve your application tests using our Mockito guide:

Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Reactive – NPI EA (cat=Reactive)
announcement - icon

Spring 5 added support for reactive programming with the Spring WebFlux module, which has been improved upon ever since. Get started with the Reactor project basics and reactive programming in Spring Boot:

>> Join Pro and download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Jackson – NPI EA (cat=Jackson)
announcement - icon

Do JSON right with Jackson

Download the E-book

eBook – HTTP Client – NPI EA (cat=Http Client-Side)
announcement - icon

Get the most out of the Apache HTTP Client

Download the E-book

eBook – Maven – NPI EA (cat = Maven)
announcement - icon

Get Started with Apache Maven:

Download the E-book

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

eBook – RwS – NPI EA (cat=Spring MVC)
announcement - icon

Building a REST API with Spring?

Download the E-book

Course – LS – NPI EA (cat=Jackson)
announcement - icon

Get started with Spring and Spring Boot, through the Learn Spring course:

>> LEARN SPRING
Course – RWSB – NPI EA (cat=REST)
announcement - icon

Explore Spring Boot 3 and Spring 6 in-depth through building a full REST API with the framework:

>> The New “REST With Spring Boot”

Course – LSS – NPI EA (cat=Spring Security)
announcement - icon

Yes, Spring Security can be complex, from the more advanced functionality within the Core to the deep OAuth support in the framework.

I built the security material as two full courses - Core and OAuth, to get practical with these more complex scenarios. We explore when and how to use each feature and code through it on the backing project.

You can explore the course here:

>> Learn Spring Security

Course – LSD – NPI EA (tag=Spring Data JPA)
announcement - icon

Spring Data JPA is a great way to handle the complexity of JPA with the powerful simplicity of Spring Boot.

Get started with Spring Data JPA through the guided reference course:

>> CHECK OUT THE COURSE

Partner – MongoDB – NPI EA (tag=MongoDB)
announcement - icon

Traditional keyword-based search methods rely on exact word matches, often leading to irrelevant results depending on the user's phrasing.

By comparison, using a vector store allows us to represent the data as vector embeddings, based on meaningful relationships. We can then compare the meaning of the user’s query to the stored content, and retrieve more relevant, context-aware results.

Explore how to build an intelligent chatbot using MongoDB Atlas, Langchain4j and Spring Boot:

>> Building an AI Chatbot in Java With Langchain4j and MongoDB Atlas

Partner – LambdaTest – NPI EA (cat=Testing)
announcement - icon

Accessibility testing is a crucial aspect to ensure that your application is usable for everyone and meets accessibility standards that are required in many countries.

By automating these tests, teams can quickly detect issues related to screen reader compatibility, keyboard navigation, color contrast, and other aspects that could pose a barrier to using the software effectively for people with disabilities.

Learn how to automate accessibility testing with Selenium and the LambdaTest cloud-based testing platform that lets developers and testers perform accessibility automation on over 3000+ real environments:

Automated Accessibility Testing With Selenium

1. Overview

In this tutorial, we’re going to see how the JVM lays out objects and arrays in the heap.

First, we’ll start with a little bit of theory. Then, we’ll explore the different object and array memory layouts in different circumstances.

Usually, the memory layout of run-time data areas is not part of the JVM specification and is left to the discretion of the implementor. Therefore, each JVM implementation may have a different strategy to layout objects and arrays in memory. In this tutorial, we’re focusing on one specific JVM implementation: the HotSpot JVM.

We also may use the JVM and HotSpot JVM terms interchangeably.

2. Ordinary Object Pointers (OOPs)

The HotSpot JVM uses a data structure called Ordinary Object Pointers (OOPS) to represent pointers to objects. All pointers (both objects and arrays) in the JVM are based on a special data structure called oopDescEach oopDesc describes the pointer with the following information:

The mark word describes the object header. The HotSpot JVM uses this word to store identity hashcode, biased locking pattern, locking information, and GC metadata. 

Moreover, the mark word state only contains a uintptr_ttherefore, its size varies between 4 and 8 bytes in 32-bit and 64-bit architectures, respectively. Also, the mark word for biased and normal objects are different. However, we’ll only consider normal objects as Java 15 is going to deprecate biased locking.

Additionally, the klass word encapsulates the language-level class information such as class name, its modifiers, superclass info, and so on.

For normal objects in Java, represented as instanceOop, the object header consists of mark and klass words plus possible alignment paddings. After the object header, there may be zero or more references to instance fields. So, that’s at least 16 bytes in 64-bit architectures because of 8 bytes of the mark, 4 bytes of klass, and another 4 bytes for padding.

For arrays, represented as arrayOopthe object header contains a 4-byte array length in addition to mark, klass, and paddings. Again, that would be at least 16 bytes because of 8 bytes of the mark, 4 bytes of klass, and another 4 bytes for the array length.

Now that we know enough about theory, let’s see how memory layout works in practice.

3. Setting Up JOL

To inspect the memory layout of objects in the JVM, we’re going to use the Java Object Layout (JOL) quite extensively. Therefore, we need to add the jol-core dependency:

<dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol-core</artifactId>
    <version>0.10</version>
</dependency>

4. Memory Layout Examples

Let’s start by looking at the general VM details:

System.out.println(VM.current().details());

This will print:

# Running 64-bit HotSpot VM.
# Objects are 8 bytes aligned.
# Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
# Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]

This means that the references take 4 bytes, booleans and bytes take 1 byte, shorts and chars take 2 bytes, ints and floats take 4 bytes, and finally, longs and doubles take 8 bytes. Interestingly, they consume the same amount of memory if we use them as array elements.

Also, if we disable compressed references via -XX:-UseCompressedOops, only the reference size changes to 8 bytes:

# Field sizes by type: 8, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
# Array element sizes: 8, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]

4.1. Basic

Let’s consider a SimpleInt class:

public class SimpleInt {
    private int state;
}

If we print its class layout:

System.out.println(ClassLayout.parseClass(SimpleInt.class).toPrintable());

We would see something like:

SimpleInt object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0    12        (object header)                           N/A
     12     4    int SimpleInt.state                           N/A
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

As shown above, the object header is 12 bytes, including 8 bytes of the mark and 4 bytes of klass. After that, we have 4 bytes for the int state. In total, any object from this class would consume 16 bytes.

Also, there is no value for the object header and the state because we’re parsing a class layout, not an instance layout.

4.2. Identity Hash Code

The hashCode() is one of the common methods for all Java objects. When we don’t declare a hashCode() method for a class, Java will use the identity hash code for it. 

The identity hash code won’t change for an object during its lifetime. Therefore, the HotSpot JVM stores this value in the mark word once it’s computed.

Let’s see the memory layout for an object instance:

SimpleInt instance = new SimpleInt();
System.out.println(ClassLayout.parseInstance(instance).toPrintable());

The HotSpot JVM computes the identity hash code lazily:

SimpleInt object internals:
 OFFSET  SIZE   TYPE DESCRIPTION               VALUE
      0     4        (object header)           01 00 00 00 (00000001 00000000 00000000 00000000) (1) # mark
      4     4        (object header)           00 00 00 00 (00000000 00000000 00000000 00000000) (0) # mark
      8     4        (object header)           9b 1b 01 f8 (10011011 00011011 00000001 11111000) (-134145125) # klass
     12     4    int SimpleInt.state           0
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

As shown above, the mark word currently doesn’t seem to store anything significant yet.

However, this will change if we call the System.identityHashCode() or even Object.hashCode() on the object instance:

System.out.println("The identity hash code is " + System.identityHashCode(instance));
System.out.println(ClassLayout.parseInstance(instance).toPrintable());

Now, we can spot the identity hash code as part of the mark word:

The identity hash code is 1702146597
SimpleInt object internals:
 OFFSET  SIZE   TYPE DESCRIPTION               VALUE
      0     4        (object header)           01 25 b2 74 (00000001 00100101 10110010 01110100) (1957831937)
      4     4        (object header)           65 00 00 00 (01100101 00000000 00000000 00000000) (101)
      8     4        (object header)           9b 1b 01 f8 (10011011 00011011 00000001 11111000) (-134145125)
     12     4    int SimpleInt.state           0

The HotSpot JVM stores the identity hashcode as “25 b2 74 65” in the mark word. The most significant byte is 65 since the JVM stores that value in little-endian format. Therefore, to recover the hash code value in decimal (1702146597), we have to read the “25 b2 74 65” byte sequence in reverse order:

65 74 b2 25 = 01100101 01110100 10110010 00100101 = 1702146597

4.3. Alignment

By default, the JVM adds enough padding to the object to make its size a multiple of 8.

For instance, consider the SimpleLong class:

public class SimpleLong {
    private long state;
}

If we parse the class layout:

System.out.println(ClassLayout.parseClass(SimpleLong.class).toPrintable());

Then JOL will print the memory layout:

SimpleLong object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0    12        (object header)                           N/A
     12     4        (alignment/padding gap)                  
     16     8   long SimpleLong.state                          N/A
Instance size: 24 bytes
Space losses: 4 bytes internal + 0 bytes external = 4 bytes total

As shown above, the object header and the long state consume 20 bytes in total. To make this size a multiple of 8 bytes, the JVM adds 4 bytes of padding.

We can also change the default alignment size via the -XX:ObjectAlignmentInBytes tuning flag. For instance, for the same class, the memory layout with -XX:ObjectAlignmentInBytes=16 would be:

SimpleLong object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0    12        (object header)                           N/A
     12     4        (alignment/padding gap)                  
     16     8   long SimpleLong.state                          N/A
     24     8        (loss due to the next object alignment)
Instance size: 32 bytes
Space losses: 4 bytes internal + 8 bytes external = 12 bytes total

The object header and the long variable still consume 20 bytes in total. So, we should add 12 more bytes to make it a multiple of 16.

As shown above, it adds 4 internal padding bytes to start the long variable at offset 16 (enabling more aligned access). Then It adds the remaining 8 bytes after the long variable.

4.4. Field Packing

When a class has multiple fields, the JVM may distribute those fields in such a way as to minimize padding waste. For example, consider the FieldsArrangement class:

public class FieldsArrangement {
    private boolean first;
    private char second;
    private double third;
    private int fourth;
    private boolean fifth;
}

The field declaration order and their order in memory layout are different:

OFFSET  SIZE      TYPE DESCRIPTION                               VALUE
      0    12           (object header)                           N/A
     12     4       int FieldsArrangement.fourth                  N/A
     16     8    double FieldsArrangement.third                   N/A
     24     2      char FieldsArrangement.second                  N/A
     26     1   boolean FieldsArrangement.first                   N/A
     27     1   boolean FieldsArrangement.fifth                   N/A
     28     4           (loss due to the next object alignment)

The main motivation behind this is to minimize padding waste.

4.5. Locking

The JVM also maintains the lock information inside the mark word. Let’s see this in action:

public class Lock {}

If we create an instance of this class, the memory layout for it would be:

Lock object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           01 00 00 00 
      4     4        (object header)                           00 00 00 00
      8     4        (object header)                           85 23 02 f8
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes

However, if we synchronize on this instance:

synchronized (lock) {
    System.out.println(ClassLayout.parseInstance(lock).toPrintable());
}

The memory layout changes to:

Lock object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           f0 78 12 03
      4     4        (object header)                           00 70 00 00
      8     4        (object header)                           85 23 02 f8
     12     4        (loss due to the next object alignment)

As shown above, the bit-pattern for the mark word changes when we’re holding the monitor lock.

4.6. Age and Tenuring

To promote an object to the old generation (in generational GCs, of course), the JVM needs to keep track of the number of survivals for each object. As mentioned earlier, the JVM also maintains this information inside the mark word.

To simulate minor GCs, we’re going to create lots of garbage by assigning an object to a volatile variable. This way we can prevent possible dead code eliminations by the JIT compiler:

volatile Object consumer;
Object instance = new Object();
long lastAddr = VM.current().addressOf(instance);
ClassLayout layout = ClassLayout.parseInstance(instance);

for (int i = 0; i < 10_000; i++) {
    long currentAddr = VM.current().addressOf(instance);
    if (currentAddr != lastAddr) {
        System.out.println(layout.toPrintable());
    }

    for (int j = 0; j < 10_000; j++) {
        consumer = new Object();
    }

    lastAddr = currentAddr;
}

Every time a live object’s address changes, that’s probably because of minor GC and movement between survivor spaces. For each change, we also print the new object layout to see the aging object.

Here’s how the first 4 bytes of the mark word changes over time:

09 00 00 00 (00001001 00000000 00000000 00000000)
              ^^^^
11 00 00 00 (00010001 00000000 00000000 00000000)
              ^^^^
19 00 00 00 (00011001 00000000 00000000 00000000)
              ^^^^
21 00 00 00 (00100001 00000000 00000000 00000000)
              ^^^^
29 00 00 00 (00101001 00000000 00000000 00000000)
              ^^^^
31 00 00 00 (00110001 00000000 00000000 00000000)
              ^^^^
31 00 00 00 (00110001 00000000 00000000 00000000)
              ^^^^

4.7. False Sharing and @Contended

The jdk.internal.vm.annotation.Contended annotation (or sun.misc.Contended on Java 8) is a hint for the JVM to isolate the annotated fields to avoid false sharing.

Put simply, the Contended annotation adds some paddings around each annotated field to isolate each field on its own cache line. Consequently, this will impact the memory layout.

To better understand this, let’s consider an example:

public class Isolated {

    @Contended
    private int v1;

    @Contended
    private long v2;
}

If we inspect the memory layout of this class, we’ll see something like:

Isolated object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0    12        (object header)                           N/A
     12   128        (alignment/padding gap)                  
    140     4    int Isolated.i                                N/A
    144   128        (alignment/padding gap)                  
    272     8   long Isolated.l                                N/A
Instance size: 280 bytes
Space losses: 256 bytes internal + 0 bytes external = 256 bytes total

As shown above, the JVM adds 128 bytes of padding around each annotated field. Cache line size in most modern machines is around 64/128 bytes, hence the 128 bytes padding. Of course, we can control the Contended padding size with -XX:ContendedPaddingWidth tuning flag.

Please note that the Contended annotation is JDK internal, therefore we should avoid using it.

Also, we should run our code with the -XX:-RestrictContended tuning flag; otherwise, the annotation wouldn’t take effect. Basically, by default, this annotation is meant for internal-only usage, and disabling the RestrictContended will unlock this feature for public APIs.

4.8. Arrays

As we mentioned before, the array length is also part of the array oop. For instance, for a boolean array containing 3 elements:

boolean[] booleans = new boolean[3];
System.out.println(ClassLayout.parseInstance(booleans).toPrintable());

The memory layout looks like:

[Z object internals:
 OFFSET  SIZE      TYPE DESCRIPTION                               VALUE
      0     4           (object header)                           01 00 00 00 # mark
      4     4           (object header)                           00 00 00 00 # mark
      8     4           (object header)                           05 00 00 f8 # klass
     12     4           (object header)                           03 00 00 00 # array length
     16     3   boolean [Z.<elements>                             N/A
     19     5           (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 5 bytes external = 5 bytes total

Here, we have 16 bytes of object header containing 8 bytes of mark word, 4 bytes of klass word, and 4 bytes of length. Immediately after the object header, we have 3 bytes for a boolean array with 3 elements.

4.9. Compressed References

So far, our examples were executed in a 64-bit architecture with compressed references enabled.

With 8 bytes alignment, we can use up to 32 GB of heap with compressed references. If we go beyond this limitation or even disable the compressed references manually, then the klass word would consume 8 bytes instead of 4.

Let’s see the memory layout for the same array example when the compressed oops are disabled with the -XX:-UseCompressedOops tuning flag:

[Z object internals:
 OFFSET  SIZE      TYPE DESCRIPTION                               VALUE
      0     4           (object header)                           01 00 00 00 # mark
      4     4           (object header)                           00 00 00 00 # mark
      8     4           (object header)                           28 60 d2 11 # klass
     12     4           (object header)                           01 00 00 00 # klass
     16     4           (object header)                           03 00 00 00 # length
     20     4           (alignment/padding gap)                  
     24     3   boolean [Z.<elements>                             N/A
     27     5           (loss due to the next object alignment)

As promised, now there are 4 more bytes for the klass word.

5. Conclusion

In this tutorial, we saw how the JVM lays out objects and arrays in the heap.

For a more detailed exploration, it’s highly recommended to check out the oops section of the JVM source code. Also, Aleksey Shipilëv has a much more in-depth article in this area.

Moreover, more examples of JOL are available as part of the project source code.

The code backing this article is available on GitHub. Once you're logged in as a Baeldung Pro Member, start learning and coding on the project.
Baeldung Pro – NPI EA (cat = Baeldung)
announcement - icon

Baeldung Pro comes with both absolutely No-Ads as well as finally with Dark Mode, for a clean learning experience:

>> Explore a clean Baeldung

Once the early-adopter seats are all used, the price will go up and stay at $33/year.

Partner – Microsoft – NPI EA (cat = Baeldung)
announcement - icon

Azure Container Apps is a fully managed serverless container service that enables you to build and deploy modern, cloud-native Java applications and microservices at scale. It offers a simplified developer experience while providing the flexibility and portability of containers.

Of course, Azure Container Apps has really solid support for our ecosystem, from a number of build options, managed Java components, native metrics, dynamic logger, and quite a bit more.

To learn more about Java features on Azure Container Apps, visit the documentation page.

You can also ask questions and leave feedback on the Azure Container Apps GitHub page.

Partner – Microsoft – NPI EA (cat = Spring Boot)
announcement - icon

Azure Container Apps is a fully managed serverless container service that enables you to build and deploy modern, cloud-native Java applications and microservices at scale. It offers a simplified developer experience while providing the flexibility and portability of containers.

Of course, Azure Container Apps has really solid support for our ecosystem, from a number of build options, managed Java components, native metrics, dynamic logger, and quite a bit more.

To learn more about Java features on Azure Container Apps, visit the documentation page.

You can also ask questions and leave feedback on the Azure Container Apps GitHub page.

Partner – Orkes – NPI EA (cat = Spring)
announcement - icon

Modern software architecture is often broken. Slow delivery leads to missed opportunities, innovation is stalled due to architectural complexities, and engineering resources are exceedingly expensive.

Orkes is the leading workflow orchestration platform built to enable teams to transform the way they develop, connect, and deploy applications, microservices, AI agents, and more.

With Orkes Conductor managed through Orkes Cloud, developers can focus on building mission critical applications without worrying about infrastructure maintenance to meet goals and, simply put, taking new products live faster and reducing total cost of ownership.

Try a 14-Day Free Trial of Orkes Conductor today.

Partner – Orkes – NPI EA (tag = Microservices)
announcement - icon

Modern software architecture is often broken. Slow delivery leads to missed opportunities, innovation is stalled due to architectural complexities, and engineering resources are exceedingly expensive.

Orkes is the leading workflow orchestration platform built to enable teams to transform the way they develop, connect, and deploy applications, microservices, AI agents, and more.

With Orkes Conductor managed through Orkes Cloud, developers can focus on building mission critical applications without worrying about infrastructure maintenance to meet goals and, simply put, taking new products live faster and reducing total cost of ownership.

Try a 14-Day Free Trial of Orkes Conductor today.

eBook – HTTP Client – NPI EA (cat=HTTP Client-Side)
announcement - icon

The Apache HTTP Client is a very robust library, suitable for both simple and advanced use cases when testing HTTP endpoints. Check out our guide covering basic request and response handling, as well as security, cookies, timeouts, and more:

>> Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

Partner – MongoDB – NPI EA (tag=MongoDB)
announcement - icon

Traditional keyword-based search methods rely on exact word matches, often leading to irrelevant results depending on the user's phrasing.

By comparison, using a vector store allows us to represent the data as vector embeddings, based on meaningful relationships. We can then compare the meaning of the user’s query to the stored content, and retrieve more relevant, context-aware results.

Explore how to build an intelligent chatbot using MongoDB Atlas, Langchain4j and Spring Boot:

>> Building an AI Chatbot in Java With Langchain4j and MongoDB Atlas

Course – LS – NPI EA (cat=REST)

announcement - icon

Get started with Spring Boot and with core Spring, through the Learn Spring course:

>> CHECK OUT THE COURSE

eBook Jackson – NPI EA – 3 (cat = Jackson)