DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Java, Spring Boot, and MongoDB: Performance Analysis and Improvements
  • Protect Your Invariants!
  • All You Need To Know About Garbage Collection in Java
  • Performance Engineering Management: A Quick Guide

Trending

  • Revolutionizing Financial Monitoring: Building a Team Dashboard With OpenObserve
  • The Role of Functional Programming in Modern Software Development
  • SQL Server Index Optimization Strategies: Best Practices with Ola Hallengren’s Scripts
  • Rust and WebAssembly: Unlocking High-Performance Web Apps
  1. DZone
  2. Coding
  3. Java
  4. Java Threads: Are They Memory Efficient?

Java Threads: Are They Memory Efficient?

A developer runs through an experiment that tested the memory use of Java threads and how to optimize your use of Java threads in your code.

By 
Ram Lakshmanan user avatar
Ram Lakshmanan
DZone Core CORE ·
Feb. 22, 21 · Tutorial
Likes (5)
Comment
Save
Tweet
Share
7.7K Views

Join the DZone community and get the full member experience.

Join For Free

Java applications tend to contain hundreds (sometimes thousands) of threads. The majority of these threads are in a WAITING or TIMED_WAITING (i.e., dormant) state, while only a small portion are actively executing lines of code. So, we were curious to know whether dormant threads consume less memory than active threads.

To figure out the answer to this question, we conducted a small study. 

What Is Stored in the Thread’s Stack?

Before you read further, you should first know what information is stored in the thread’s stack. To get a complete picture of what information is stored in the thread’s stack, you can refer to this article. In a nutshell, the following is stored in the thread’s stack:

  1. Local variables that are created in the method.
  2. The code path that is currently being executed by the thread.

Study

To facilitate our study, we wrote two simple programs. Let’s review these two programs and their performance characteristics.

1. Threads With Empty Stack Frames

We created a simple Java program that will create 1000 threads. All the threads in this program will have their stack frames almost empty so that it doesn’t have to consume any memory.

Java
 




x
24


 
1
public class EmptyStackFrameProgram {   
2

          
3
 public void start() {           
4

          
5
   // Create 1000 threads      
6
   for (int counter = 0; counter < 1000; ++counter) {         
7
        new EmptyStackFrameThread().start();      
8
     }         
9
   }
10
 }
11

          
12
 public class EmptyStackFrameThread extends Thread {    
13

          
14
 public void run() {        
15

          
16
 try {                  
17
     // Just sleep forever         
18
     while (true) {            
19
     Thread.sleep(10000);         
20
    }     
21
 } catch (Exception e) {      
22
     }   
23
   } 
24
}


In this Java program, 1000 threads are created in the EmptyStackFrameProgram class. All the EmptyStackFrameThread threads go into an infinite sleep, and they don’t do anything. This means that their stack frame will be almost empty because they aren’t executing any new lines of code or creating any new local variables. 

Note: We are putting the threads into infinite sleep so they will not die down, which is essential to study their memory usage.

2. Threads With Loaded Stack Frames

Here is another simple Java program that will create 1000 threads. All the threads in this program will have their stack frames fully loaded with data so that they will consume a lot more memory than the earlier program.

Java
 




x
45


 
1
public class FullStackFrameProgram {    
2
   public void start() {               
3

          
4
   // Create 1000 threads with full stack      
5
   for (int counter = 0; counter < 1000; ++counter) {         
6
       new FullStackFrameThread().start();      
7
     }   
8
   } 
9
}
10
 public class FullStackFrameThread extends Thread {    
11

          
12
    public void run() {   
13

          
14
       try {         
15
          int x = 0;         
16
          simpleMethod(x);      
17
          } catch (Exception e) {     
18
     }   
19
}   
20

          
21
 /**    
22
  * Loop for 10,000 times and then sleep. So that stack will be filled up.    
23
  *    
24
  * @param counter    
25
  * @throws Exception    
26
  */   
27

          
28
private void simpleMethod(int x) throws Exception {       
29

          
30
    // Creating local variables to fill up the stack.     
31
    float y = 1.2f * x;      
32
    double z = 1.289898d * x;            
33

          
34
   // Looping for 10,000 iterations to fill up the stack.            
35
   if (x < 10000) {        
36
       simpleMethod(++x);      
37
   }            
38

          
39
   // After 10,000 iterations, sleep forever      
40
   while (true) {         
41
      Thread.sleep(10000);      
42
    }         
43
  } 
44
}


In this Java program, 1000 threads are created in the FullStackFrameProgram class. All the FullStackFrameThread threads invoke the simpleMethod(int counter)10,000 times. After 10,000 invocations, threads will go into infinite sleep. Since the thread is invoking the simpleMethod(int counter), 10,000 times, each thread will have 10,000 stack frames and each stack frame will be filled up with local variables ‘x’, ‘y’, ‘z’. 

                               Fig: EmptyStackFrameThread and FullStackFrameThread stack

The above diagram shows the visualization of EmptyStackFrameThread’s stack and FullStackFrameThread’s stack. You can notice EmptyStackFrameThread contains only two stack frames. On the other hand, FullStackFrameThread contains 10,000+ stack frames. Besides that, each stack frame of FullStackFrameThread will contain the local variables x, y, z. This will cause the FullStackFrameThread stack to be fully loaded. Thus, one would expect the FullStackFrameThread stack to consume more memory.

Memory Consumption

We executed the above two programs with following settings:

  1. Configured the thread’s stack size to 2 MB (i.e., passed -Xss2m JVM argument to both programs). 
  2. Used OpenJDK 1.8.0_265, 64-Bit Server VM. 
  3. Run both the programs simultaneously on AWS ‘t3a.medium’ EC2 instances.

Below you can see the program’s memory consumption reported by the system monitoring tool ‘top’ .

                                      Fig: Memory consumption of both programs are same

You'll notice both the programs are consuming exactly 4686 MB of memory. This indicates both program threads consume the same amount of memory, even though FullStackFrameThread is hyperactive, whereas EmptyStackFrameThread is almost dormant.

To validate this theory, we further analyzed both the programs using the JVM root cause analysis tool yCrash. Below is the thread analysis report generated by the yCrash tool.

                         Fig: yCrash reporting that EmptyStackFrameThread stack is almost empty


                         Fig: yCrash reporting that the FullStackFrameThread stack is almost full

yCrash also clearly points out that EmptyStackFrameProgram contains 1,000 threads with two stack frames, whereas FullStackFrameProgram contains 1,000 threads with 10,000 stack frames.

Conclusion

This study clearly indicates that memory is allocated to threads at the time of creation, and memory is not allocated based on the thread’s runtime demand. Both hyper working threads and almost dormant threads consume the same amount of memory. Modern Java applications tend to create hundreds (sometimes thousands) of threads. But most of those threads are in a WAITING or TIMED_WAITING state and do nothing. Given that threads occupy the maximum amount of allocated memory right when they are created, as an application developer you can do the following to optimize your application’s memory consumption:

  1. Create only the *necessary* threads for your application. 
  1. Try to come up with optimal stack size (i.e., -Xss) for your application threads. So, if you are configuring a thread’s stack size (i.e., -Xss) to 2 MB, and, in runtime, your application uses only 512 KB, you will be wasting 1.5 MB of memory for each thread in your application. If your application has 500 threads, you will be wasting 750 MB (i.e., 500 threads x 1.5 MB) of memory per JVM instance, which is not cheap in the modern cloud computing era.

You can use tools like yCrash to tell you how many threads are active and how many threads are in a dormant state. It can also tell you how deep each thread’s stack goes. Based on these reports, you can come up with an optimal thread count and a thread’s stack size for your application.

Memory (storage engine) Java (programming language) application Frame (networking)

Opinions expressed by DZone contributors are their own.

Related

  • Java, Spring Boot, and MongoDB: Performance Analysis and Improvements
  • Protect Your Invariants!
  • All You Need To Know About Garbage Collection in Java
  • Performance Engineering Management: A Quick Guide

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!