Zakaria Ournani
email: zakaria.ournani@inria.fr

Romain Rouvoy
email: romain.rouvoy@univ-lille.fr

Pierre Rust
email: pierre.rust@orange.com

Joel Penhoat
email: joel.penhoat@orange.com

Evaluating The Energy Consumption of Java I/O APIs

Keywords: Energy consumption, Java, I/O

The Java language is rich of native and third-party I/O APIs that most Java applications and software use. Such operations can even be considered core to most software as they allow the interaction with the user and its data in a nonvolatile way. Yet, the I/O captivate a lot of attention due to their importance, but also due to the cost that these relatively slow operations add to read and write precious data, most commonly from/to disks. In this context, the impact of these I/O operations on energy consumption didn't get as much attention. Of course, I/O operations are responsible for energy consumption at the level of the storage medium (HDD or SSD) but they also induce non-negligible costs -both performance and energy-wise-at the CPU level. However, only few works take into account the impact of I/O on the energy consumption, especially at the CPU-level.

Hence, this paper elaborates a detailed study with two main objectives. First we aim at assessing the energy consumption of several well-known I/O libraries methods, and investigate if different read/write methods can exhibit different energy consumption. Concretely, we assess -using micro-benchmarksthe energy consumption of 27 I/O methods for several file sizes and establish the truth about the most and least energy efficient methods. The second objective is to validate the results of the first experiments on real Java projects by substituting their default I/O methods and measuring the before/after energy consumption.

Our results showed that i) different I/O methods consume very different amounts of energy, such as NIO Channels that are 20% more efficient than other methods for read purposes ii) substituting the I/O method in a software by a more efficient one can save an important amount of energy, 15% of energy saving has been registered for K-nucleotide and 3% for Zip4j. We also showed that choosing the right I/O method can save more than 30% of energy consumption when using the Javax.crypto API.

Our work offers direct conclusions and guidelines on which I/O methods to use in which situation (read all data, read specific data, write data, etc.) for a better energy efficiency. It also opens doors for other works to better optimize the energy consumption of the I/O APIs and methods.

I. INTRODUCTION

Energy efficiency of software systems is undoubtedly a major challenge for the software engineering community. Beyond state-of-the-art key performance indicators, such as latency, throughput, scalability or availability, energy consumption challenges developers to reach the best performances while minimizing the subsequent resource requirements. In this context, previous studies in the field have been focusing on the impact of algorithms [START_REF] Jagroep | Energy efficiency on the product roadmap: An empirical study across releases of a software product: Energy efficiency on the product roadmap[END_REF], [START_REF] Manotas | An empirical study of practitioners' perspectives on green software engineering[END_REF] and data structures [START_REF] Hasan | Energy Profiles of Java Collections Classes[END_REF], [START_REF] Cruz | Leafactor: Improving Energy Efficiency of Android Apps via Automatic Refactoring[END_REF] to reduce the energy consumption in presence of computation-intensive applications. Nevertheless, little effort has been invested in the study of the energy efficiency of Java I/O libraries [START_REF] Rocha | Comprehending energy behaviors of java i/o apis[END_REF] for the purpose of data-intensive systems/applications. Dataintensive applications are expected to process huge amounts of data for different purposes, from big data analytics to online cloud microservices. While the hardware components tend to keep improving on storage capacity and throughput, it remains unclear if their software counterparts succeed to keep the pace and provide energy-efficient solutions to efficiently read and write data. This is particularly difficult in Java, which provides a vast ecosystem of built-in functions and third-parties libraries to interact with persistent storage. In addition to this rich ecosystem, I/O APIs may be subject to the emergence of other features, like asynchronous capabilities (NIO), which can exhibit a different energy footprint. We believe that guiding the developers in picking the most energy efficient I/O method is a key challenge to deliver sustainable software with no compromise on the performances.

In this area, previous works have already shown the substantial contribution of I/O to the global energy consumption of software. For example, Lyu et al. [START_REF] Lyu | An empirical study of local database usage in android applications[END_REF] indicated that 10% of the mobile battery drains due to I/O operations. The energy consumption of Java I/O APIs has also been compared by Rocha et al. [START_REF] Rocha | Comprehending energy behaviors of java i/o apis[END_REF]. They reported that the energy consumption can widely vary among these APIs, and that the most popular APIs are not always the most energy efficient. This study delivers interesting insights about some APIs, such as java.io.In(Out)putStream and java.io.Reader(Writer), as well as their inherited classes. However, it lacks some considerations for major I/O APIs, such as channels (Java.nio.FileChannel) or other popular third-party libraries (e.g., Apache, Google Guava), it also did not consider different usage profiles of I/O, and failed to reproduce the experiments on a realistic Java project.

In this paper, we therefore assess the energy consumption of 27 different I/O methods issued from multiple native and thirdparty libraries using micro-benchmarks and different workloads. These methods are tested and compared for different scenarios and use cases (read the whole file, read a file part-bypart, seek data from a file, write data in a file, using a different buffer sizes, etc.). Concretely, the purpose of the study is to answer the following research questions:

RQ 1: How do I/O methods affect the energy consumption of a Java code? RQ 2: Can we reduce the energy consumption of software by substituting its I/O methods? Our empirical exploration highlights the most energy-efficient methods for each use case. For instance, using channels for massive reads is usually 10-20% more energy efficient than other I/O methods. Moreover, we substituted the default I/O methods of well-known Java benchmarks and projects to effectively reduce their energy consumption.

Beyond answering these two research questions, the contributions of this paper can be summarized as:

1) Elucidate the energetic behavior of 27 different I/O methods issued from multiple libraries, using several file sizes, 2) Identify the most energy-efficient methods for several read and write use-cases, 3) Model the energy consumption in regards to the buffer size for the buffered I/O methods, 4) Deliver insights and guidelines to summarize the results and conclusion of our experiments, 5) Investigate the potential gain of substituting the default I/O methods of benchmarks and real java projects. The remainder of this paper is organized as follows. Section II discusses the related works about energy consumption in Java software systems. Section III introduces the methodology (hardware, projects and experiments design) we adopted in this study. Section IV analyzes several experiments we conducted to evaluate the energy consumption of Java I/O methods, as well as the results we observed during these experiments. Finally, Sections V and VI cover the threats to validity and our conclusions, respectively.

II. RELATED WORKS

In this section, we review the state of the art related to Java energy efficiency. The energy consumption of Java applications and the JVM platforms gained interest recently. In particular, many studies have been conducted to reduce the energy consumption of Java applications from servers [START_REF] Longo | Reducing energy usage in resource-intensive Java-based scientific applications via microbenchmark based code refactorings[END_REF], [START_REF] Lafond | An Energy Consumption Model for an Embedded Java Virtual Machine[END_REF], [START_REF] Brownlee | Search-Based Energy Optimization of Some Ubiquitous Algorithms[END_REF], [START_REF] Fernandes | Assisting Non-Specialist Developers to Build Energy-Efficient Software[END_REF], [START_REF] Ournani | On reducing the energy consumption of software: From hurdles to requirements[END_REF] to mobile devices [START_REF] Morales | EARMO: An Energy-Aware Refactoring Approach for Mobile Apps[END_REF], [START_REF] Moreira | Overviewing the liveness of refactoring for energy efficiency[END_REF], [START_REF] Iannone | Refactoring android-specific energy smells: A plugin for android studio[END_REF], [START_REF] Linares-Vásquez | Multi-objective optimization of energy consumption of guis in android apps[END_REF].

For example, the energy consumption of Java collections and data structures has been largely discussed by many papers [START_REF] Daylight | Incorporating energy efficient data structures into modular software implementations for internet-based embedded systems[END_REF], [START_REF] Hunt | Characterizing the performance and energy efficiency of lock-free data structures[END_REF], [START_REF] Manotas | Seeds: A software engineer's energy-optimization decision support framework[END_REF], [START_REF] Fernandes | Assisting Non-Specialist Developers to Build Energy-Efficient Software[END_REF]. Pinto et al. [START_REF] Pinto | A Comprehensive Study on the Energy Efficiency of Java's Thread-Safe Collections[END_REF] studied the effect of Java collections on energy consumption, including parameters, such as the collection size and the most executed operations (insertion, removal, search). Hasan et al. [START_REF] Hasan | Energy Profiles of Java Collections Classes[END_REF]compared the energy consumption of several Java data structures. They analyzed 6 real projects and measured the evolution of the energy consumption in different scenarios (insertion at the beginning, iteration, etc.). Their study showed that wrongly choosing the implementation behind a collection may degrade the energy consumption by up to 300%. Other works [START_REF] Pereira | Jstanley: Placing a green thumb on java collections[END_REF], [START_REF] Oliveira | Recommending energy-efficient java collections[END_REF] even delivered tools to enhance the energy consumption by replacing the used collections for several scenarios.

The energy consumption of Java code refactorings is another major issue that has been extensively studied [START_REF] Ournani | Tales from the code #1: The effective impact of code refactorings on software energy consumption[END_REF], [START_REF] Park | Investigation for Software Power Consumption of Code Refactoring Techniques[END_REF], [START_REF] Kumar | Energy consumption in Java: An early experience[END_REF], [START_REF] Ouni | Multi-Criteria Code Refactoring Using Search-Based Software Engineering: An Industrial Case Study[END_REF]. Sahin et al. studied the impact of 6 refactoring rules on a total of 197 code snippets found in 9 Java applications. Their results showed that the impact of applying the refactoring could be statically significant, but is not consistent across software and platform versions. They suggested that knowledge on the impact of refactoring rules on the energy consumption could be integrated within IDEs to help developers in building less energy-bleeding software. In a more detailed study of the impact of a single refactoring ruleknown as "inline method"-on 3 Java applications, the authors [START_REF] Silva | Evaluation of the impact of code refactoring on embedded software efficiency[END_REF] reported that the impact on the execution time and energy consumption that was expected to be positive, was not always true. In another paper, Pereira et al. [START_REF] Pereira | Helping Programmers Improve the Energy Efficiency of Source Code[END_REF] proposed SPELL, the energy leaks detector tool. The tool uses JRAPL [START_REF] Liu | Data-oriented characterization of application-level energy optimization[END_REF] to measure energy consumption and detect energy inefficient code fragments using a statistical spectrum-based energy red spots localization. Some preliminary tests on Java programs resulted in up to 18% energy savings.

In this context, I/O have only been weakly addressed with a few studies focusing on the performance of Java I/O. For example, Karabyn et al. [START_REF] Karabyn | Performance and scalability analysis of Java IO and NIO based server models, their implementation and comparison[END_REF] deeply compared the performance and scalability of Java IO and Java NIO classes within servers. Their results showcased that-for long-lived connections-the difference in throughput is marginal, but that Java NIO has the edge when it comes to a lot of simultaneous connections and offers a minimal memory usage.

Pinto et al. discussed 12 contributions taken from the state of the art on the refactorings that could be applied to improve software energy efficiency [START_REF] Pinto | Refactoring for Energy Efficiency: A Reflection on the State of the Art[END_REF]. This literature review was conducted on the papers that were published in 8 of the top software engineering conferences prior to 2015. It summarizes insights related to CPU offloading, HTTP requests, I/O operations, DVFS techniques, etc. For I/O operations, the paper reported that I/O utilities contribute significantly to the energy consumption of mobile applications.

For Java I/O libraries' energy consumption, Rocha et al. [START_REF] Rocha | Comprehending energy behaviors of java i/o apis[END_REF] conducted a comparative study of some I/O methods, mostly I/O classes that inherit from java.io.In(Out)putStream and java.io.Reader(Writer). This preliminary study offers some interesting insights on the behavior of some of the most common native Java I/O methods. Nevertheless, the reported work lacks some of the most used I/O methods, and does not deliver guidelines to reduce the energy consumption across multiple I/O scenarios and use cases.

The objective of our work is to establish a robust assessment of the energy consumption of some mainstream native and third-party I/O libraries. Concretely, we aim at understanding the energetic impact of some I/O libraries that have not been covered, such as NIO channels, or third-party libraries, such as Apache or Guava. Moreover, we investigate multiple scenarios and use cases, such as loading the whole file in memory, reading a file by chunks, seeking specific data from a file, and writing data. We also cover the energy consumption of using different buffer sizes for buffered operations. Finally, we assess the most energy-efficient methods on acknowledged benchmarks and public Java projects to reduce the overall energy consumption by substituting default I/O methods with the ones that reported a better energy efficiency.

III. METHODOLOGY

To investigate the energy impact of different Java I/O libraries and methods, we conducted a wide set of experiments using several micro-benchmarks.

A. Environment Settings

To report on reproducible measurements, we used the same hardware configuration for all our experiments. The machine is equipped with Intel i7-6600U @ 2.60GHz, 16 GB of RAM and an Intel SSD Pro 5450s Series, with a capacity of 256 GB (up to 550 MB/s for reading and 500 MB/s for writing operations). Our experimental protocol enforces that the software under test is the only process executed on the node, configured with a very minimal version of Ubuntu 18.04 (4.15.0-140-generic kernel version) with OpenJDK 1.8.0 242. The minimal configuration of the OS indicates an operating system with only the required daemons and services. Everything else was disabled [START_REF] Ournani | Taming energy consumption variations in systems benchmarking[END_REF], to minimize the variations on the observed energy consumption. We focused on measuring the CPU energy consumption to evaluate the differences in energy consumption between the tested methods. We thus used Intel RAPL as a physical power meter to analyze the energy consumption of the CPU. RAPL is one of the most accurate tools to report on the global energy consumption of a processor [START_REF] Khan | Rapl in action: Experiences in using rapl for power measurements[END_REF], [START_REF] Desrochers | A validation of dram rapl power measurements[END_REF]. The registered CPU energy consumption includes core and uncore energy consumptions.

Moreover, we tried to be very careful, while running our experiments, not to fall in the most common benchmarking "crimes" [START_REF] Van Der Kouwe | Benchmarking Crimes: An Emerging Threat in Systems Security[END_REF]. Every single experiment, therefore, reports on energy metrics obtained from 30 executions per benchmark. All of our experiment materials are made available for use/reproducibility from https://anonymous.4open.science/ r/7f5f13601f85a2446a9f934.

B. Experiments Design

Our experiments are structured in two steps. The first step has an exploratory nature: by testing and comparing numerous Java I/O libraries and methods to read and write data from files. To do so, we prepared text and binary files of several sizes. Table I summaries the size on disk of each file type and the lines count × lines length for the equivalent text file of the same size. Next, we create micro-benchmarks for each I/O method, then run them for 30 times and measure the execution time and the CPU energy consumption. All read benchmarks run the same function consume to compute a hash from the raw input data, thus prevent the JIT from discarding some source code and altering the expected behavior.

The second step aims at validating the results of the first step using some Java benchmarks from the Computer Language Benchmarks Game (CLBG): Fasta and K-nucleotide, but also using a real Java project (Zip4J) and a real Java API (Javax.Crypto). This validation is achieved by substituting the read/write methods used in these benchmarks/projects by the ones that exhibited the lowest energy consumption in the previous step, and check whether the energy consumption of these source codes can be reduced. 1) Java I/O Libraries: We evaluated a wide set of read/write methods from several Java I/O libraries in our study. The full list of methods is provided in Table II. The purpose is not to explore every sub-classes or sub-methods of the ones present in Table II. For example, we do not test every method that extends InputStream, such as PushbackInputStream or ByteArrayInputStream. Such methods have already been compared in [START_REF] Rocha | Comprehending energy behaviors of java i/o apis[END_REF]. The purpose is rather to study and compare different methods issued from different classes, libraries, and even famous third-party solutions. Table II reports on 3 different purposes: "Read" to read data, "Seek" for accessing some data at a specific position and "Write" to write data. "ReadAll" is a particular case of Read where we read the content of the whole file at once. Most of the used classes are issued from java.io and java.nio (NIO). NIO is for non-blocking I/O-i.e., by handling the I/O operations in a non-blocking way using buffers, channels, etc. Some of the methods in Table II can use a buffer, which is a memory block of a given size (usually 8,192 bytes). Contrary to arrays or lists, a buffer has a limit that differs from its capacity. This enforces the ability to have a variable size up to the capacity, which is the maximum it can handle. Moreover, the buffer offers a built-in way to read or write the next element, easing sequential processing, and a way to save the current position for later reset (mark and position functions, respectively). We note that the versions of the used APACHE and GUAVA libraries are 2.8.0 and 23.0 respectively.

2) Real Benchmarks: The impact of I/O operations on software energy consumption mainly depends on the type of software. This impact could be high for some applications, such as file compression or serialization applications, while it can be much lower for other classes of applications that perform much less I/O operations. Hence, we choose benchmarks that stress the usage of I/O operations to confirm the results of the micro-benchmarks and clearly spot the differences in energy consumption that such operations can cause for real applications.

First, we choose 2 I/O focused benchmarks from the Computer Language Benchmarks Game: Fasta, and K-nucleotide. The first one generates DNA sequences (CGAT), by weighted random selection from 2 alphabets and outputs the results in a file. The second benchmark reads line-by-line the Fasta We substitute the write and read methods from the Fasta and K-nucleotide with the methods that exhibited the best energy efficiency from our micro-benchmarks. The purpose is to check if we can reduce the energetic impact of those benchmarks with only the substitution of the I/O method. Moreover, we apply the same substitution process on the read method of real Java projects: Zip4J1 and Javax.Crypto. 2The first project is a Java library that offers many operations for handling zips and streams. It is the only Java library with support for zip encryption, apart from several other features. The second project is a Java API that delivers many classes and interfaces for cryptographic operations. Substituting the I/O operations in this context aims to deliver a more realistic feedback on the energy impact of I/O on a library that uses a fair amount of I/O operations, such as Zip4J and Crypto.

IV. EXPERIMENTS & RESULTS

In this section, we expose the results of our experiments in order to answer our research questions.

A. Behavior of I/O Methods

In this part, we study the behavior of the considered I/O methods used in the micro-benchmarks. One benchmark has been written for each method of Table II. Then, experiments are run with each benchmark and for several file sizes (cf Table I).

1) Reading the Whole File at Once: This method is limited by the memory size-i.e., it can only be applied on small files where the RAM size would allow loading the content of the whole file at once. Some of the classes mentioned in Table II natively offer the possibility to read the whole content of a file (ReadAll methods from IOSTREAM, APACHE, NIOF, GUAVA). While we believe that this way to read files is not compatible with all needs and thus is not the most interesting, we still run a quick comparison of the available methods using the medium file at best due to the memory limitation. Figure 1 depicts the energy consumption of 5 native methods to read the whole content of binary and text (strings or lines) files issued from 4 classes. It shows that the InputStream.readAllBytes() method (IOSTREAM) is the most energy efficient, followed by APACHE ReadFileToByteArray(File, Charset)

or ReadFileToString(File).

NIO

Files.readAllLines(Path) and GUAVA readLines(File, Charset) methods consumed the most energy among the 5 tested methods.

Despiste reading being limited by the file sizes, we can still notice a substantial gain in energy consumption with the appropriate method and use case. In fact, IOSTREAM consumed 4 times less energy compared to GUAVA and 3 times less compared to NIOF, for a medium binary file. It was the most efficient way to read whole files among the 5 methods and is 60% more efficient than APACHE to read binary files for medium file sizes. To natively read text files, APACHE* was the most efficient way to do it, 40% more efficient than NIOF and 100% more efficient than GUAVA.

2) Reading the Whole File by Chunks: This way of reading is much more flexible and bypasses the memory size limitation by reading the file by chunks. Usually, this is achieved by using a buffer or an array of a given size that slides through the file to read input data. Many libraries offer functions to natively achieve this. We thus run read methods from Table II with different file sizes and assess their energy consumption. Figure 2 overviews the energy consumption of all tested read methods on several file sizes. The observed difference in energy consumption seems to be very small for our tiny, small, and medium file sizes, but the lines become clearly distinguishable as the file sizes grow. We note that two other read methods (RAF and Scanner) have been tested, but are not depicted in the figure for visualization issues. Our experiments in Table III showed that different input methods consume different energy. The most extravagant case is RANDOMACCESSFILE where we noticed an extra energy consumption, even for tiny and small files. This extra energy becomes very important for bigger files (up to 200 times more energy). The SCANNER read is the second noticeable method that gave much worse results, compared to the others for all file sizes (more than 4 times more energy consumed with Scanner).

The other results of Table III are much closer, with methods that give very similar results for all file sizes (APACHE, BFILEREADER, BIOSTREAM, GUAVA, NIOF). The clear winner here are CHANNEL and ONMEMORYCHANNEL that consumed the least energy among all methods and across all file sizes. 3 Along our experiments, NIO CHANNELS consumed up to 20% less energy, compared to the average and about 10% to the second-best method (FILEREADER), which constitute a substantial gain, especially for large files and applications that use a good amount of I/O.

Finally, we noticed that using the buffer for FILEREADER and INPUTSTREAM is not very beneficial. The buffered BFIL-EREADER and BIOSTREAM consumed up to 10% more energy for large files. Figure 3 exposes more visually the results of Table III for a large file. The violin plots show very stable energy consumption values with very small standard deviations. This reports on robust experiments, but also validates the results of Table III, as all results are tightly centered around the average/median. The figure also allows to establish an easier comparison of the read methods issued from the different classes (RAF and SCANNER have been excluded for a better visualization, OMCHANNEL is not applicable for large files).

Figure 4 depicts the average of read data per joule for each method, while reading the large file. This confirms that the CHANNEL is more efficient and reads more data per joule. In fact, the CHANNEL read method reads about 35 MB/j. That is 5 MB/j more efficient than FILEREADER and 10 MB/j better than the average. RAF and SCANNER on the other hand are the least efficient with 5 MB/j and less than 1 MB/j, respectively.

One more question we wanted to address is: what is the most energy-efficient way to read data from a file if the memory size and the JVM heap size are sufficient to load the whole file? If we compare the necessary energy consumption to read our medium file in Figure 1 and Table III, we notice that reading the whole file at once with IOSTREAM consumed about 3.8 joules. That is at least 13% less consumed energy than any other method in Table III (4.4 joules being the lowest). This indicates that reading the whole file can be more energy efficient than reading it by chunks, when possible. a) Buffer Size: Many of the previous methods require or use a buffer to perform the reading operation (BIOSTREAM, BFILEREADER, CHANNEL, OMCHANNEL, RAF, etc.). This buffer size may vary and can be set by the developer. In this experiment, we check the effect of the buffer size on the energy consumption.

Thus, we run multiple executions of the BIOSTREAM read operation (BufferedInputStream), and we vary the buffer size from its default value used for the previous experiments (8, 192 bytes). This difference is not noticeable for small files, but can be clearly seen in Figure 5 for a large file. This figure illustrates the measured energy consumption for each buffer size. We notice a parabolic shape of the values of energy consumption, approximately centered around 8192 bytes. The difference in energy consumption is very small (less than 3%) going to the nearest values to 8, 192 bytes (4, 096 bytes and 16, 384 bytes). However, this difference grows much higher for further small or big values of the buffer size. Here, up to 13-15% more energy consumption for the buffer sizes 1, 048, 576 bytes and 1, 024 bytes, respectively. Hence, the buffer size should not be too small or too big for a better energy efficiency.

3) Seeking Specific Data From a File: This represents the third possibility to read data from a file. It consists of moving a cursor or a pointer within a binary file, and read an amount of data. This way is very interesting to access some of the data within the file without reading the whole file, especially for very large files. One potential application relates to database files that contain an index, such as SQLite, where we need to access the data pointed by the index without reading the whole database.

Thus, we ran multiple experiments to compare the available seek methods for Table II and their energy consumption. To do so, we access and read different data at numerous positions within the files of multiple sizes. Each time, we read 100 bytes and move forward with 10, 000 bytes to read the next 100 bytes, until the end of the file.

Table IV and Figure 6 show the difference in energy consumption between the 3 different methods issued from CHANNELS, IOSTREAM, and RAF. Here again, the results show that using the NIO channels is the most energy-efficient way to seek data from files. This efficiency is mostly noticeable for a large file with a high number of seek operations, where CHANNELS position method is 5 times faster than IOSTREAM skip method. On the other hand, the RAF seek method is the slowest and the most energy consuming. It consumed 67 times more energy than NIO Channels for this experiment on a large file.

4) Writing a File: The write operation consists of physically writing data on a file in its disk location. Like reading, there are plenty of classes that offer write methods to achieve that purpose. Therefore, we assess the energy consumption of the write methods from Table II and try to identify the "good" and "bad" methods regarding the energy efficiency with different file sizes. Writing a file of a specific size is achieved by generating random text data of the appropriate size (LinesCount × LineLength) as described in Table I and writing it into a file. Figure 7 depicts the evolution of the consumed energy by the different methods with growing file sizes (GUAVA has been omitted for a better visualization), while Table V gives the detailed energy consumption and the execution time of each method, and for each file size.

The results highlight that 4 of the tested methods are way more energy-consuming than the others. First, GUAVA's write method registered the worst energy efficiency, compared to all other methods and, for all file sizes, up to 6 times more energy consumption compared to the best-tested methods. Second, the NIOF writing operation consumed 50-100% more energy across all file sizes. Third, the NIO channels that gave the absolute best results for file reading are less efficient for the writing operation with 30% more energy consumption for large files. RAF is in the fourth position of the methods that performed worse than the others for write operations. It consumed 15% more energy compared to the best method.

The remaining methods (APACHE, BFILEWRITER, BIOSTREAM, FILEWRITER, IOSTREAM) gave very similar results, and were the most energy-efficient methods to write data into files.

Figure 8 delivers a better comparison of the energy consumption of the write methods for a large file.

One thing we noticed by comparing Figure 3 and Figure 8 is that writing a file consumes more energy than reading a file of the same size. In fact, we needed less than 500 joules to read our large file with the most efficient read method, while the least-consuming write method needs 5 times that amount of energy (2500 joules) to write a file of the same size. This can be clearly seen through Figure 9 that shows the average written data per joule for each method. The maximum value we observe for the most energy efficient write operations is 7 MB/j. This is much lower than the values obtained for read operations (up to 35 MB/j in Figure 4).

To answer RQ1, we conclude that using different I/O methods can alter the software energy consumption. Our experiments delivered some insights and guidelines that can be summarized as: 1) For read methods, using the class nio.FileChannel proved to be the most appropriate choice to consume the least amount of energy while reading files of different sizes. It was at least 10-20% more energy efficient; 2) SCANNER and RAF reported on a very high energy consumption, compared to the other methods and should thus be carefully used, if not avoided, for data reading purposes; 3) For methods that use buffers, using buffer sizes that are too big or too small compared to the default size of 8, 192 bytes may introduce an extra cost in energy consumption; 4) Reading the whole file at once is limited by the file/memory size, but can be very energy efficient; 5) For write operations, many methods reported on a similar energy consumption, but other methods, such as GUAVA or NIOF consumed more energy, and should thus be avoided.

B. Substituting I/O Methods

In this part, we aim at answering the second research question by substituting and comparing multiple I/O methods on 4 Java projects presented in Figure 10: Fasta, Knucleotide, Zip4J, and Javax.Crypto. Thus, we substitute the write method from the Fasta program with the methods that reported on a low energy consumption from the previous step to save a 1.5 GB of generated nucleotides. We then assess the energy consumption with each of the methods used, for 30 executions each.

Figure 10a illustrates the boxplots that summarize the energy consumption of each method's execution. As seen in the previous section, the write methods are almost similar and consume approximately the same amount of energy (around 255 joules). The written Fasta data is used as an input file for the K-nucleotide program. Figure 10b depicts the energy consumption of the nucleotide read operations using the methods that we used as substitutes to the default BIOSTREAM read method (the methods that performed best on the microbenchmarks). Just like in our previous micro-benchmark experiments, the figure shows that using CHANNELS results in a lower energy consumption (15% less energy consumption using CHANNELS compared to the default BIOSTREAM).

This result can be confirmed by Figure 10c in which we substituted the reading method of the Java zipping library with other read methods susceptible of having an equivalent or better energy efficiency. Then, we ran experiments where we zipped our large file and saved the result. Here again, using CHANNELS causes a reduction of 3% in energy consumption, compared to the default INPUTSTREAM method. The gain of using channels is only 3% in this experiment because the file reading phase only covers 10-15% of the total execution time of the zipping process. Finally, Figure 10d represents another confirmation of the energy efficiency of using CHANNELS for file reading purposes. In this example, using CHANNELS was at least 30% more energy efficient than FILEREADER and IOSTREAM (50% more energy efficient than BIOSTREAM and NIOF) to decrypt a 3 GB file using the Crypto API with the AES algorithm and a 16 bytes key.

To answer RQ2, we empirically showed that we can reduce the energy consumption of software and programs that run a substantial amount of I/O by choosing the right methods. Using NIO Channels proved to be very energy efficient here again on K-nucleotide, Zip4J, and Crypto, compared to other read methods.

V. THREATS TO VALIDITY

There are a couple of issues that might impact the accuracy of our results. Starting with the use of the Intel RAPL to measure the energy consumption. Although RAPL is one of the most accurate available tools to measure the energy consumption of software [START_REF] Khan | Rapl in action: Experiences in using rapl for power measurements[END_REF], [START_REF] Desrochers | A validation of dram rapl power measurements[END_REF], it only gives the global energy consumption and no fine-grained measures at process or thread levels. Hence, we used bare-metal hardware with a minimal OS configuration. We disabled all the non-essential services and daemons to limit the overhead that the OS may add to the execution [START_REF] Ournani | Taming energy consumption variations in systems benchmarking[END_REF]. We also ran all of our experiments within the exact same node to avoid the problem of CPU energy consumption variation [START_REF] Wang | Potential effects on server power metering and modeling[END_REF], [START_REF] Ournani | Taming energy consumption variations in systems benchmarking[END_REF].

The execution time and registered energy consumption for short tasks is one more subtle threat to validity. In fact, some read and write experiments on tiny and small files are very fast, so we cannot assess the energy consumption faithfully. To overcome this issue, we constituted every execution of these fast experiments of many iterations. Most importantly, we focused all of our results analysis and conclusions on the experiments that last much longer, using our medium and large files. Java Just-in-time (JIT) compiler might constitute another threat to the validity of this work, especially for read operations in micro-benchmarks, if the read data is not used. Thus, we executed a hash method that consumes the read data similarly to a black-hole in JMH (Java Microbenchmark Harness). We also discarded the whole JVM instance between executions, so the JIT does not cache data between executions and alter the measures. We did not disable the JIT because the study would not reflect a real usage of I/O methods in realistic Java applications anymore.

The considered I/O libraries come with multiple read and write operations. To conduct this first study, we were obliged to select some of these methods. Our aim was to diversify our methods selection to deal with both binary and text files, but we also wanted to select methods with similar signatures, so we can construct a fair and relevant comparison between the I/O libraries. We plan on generalizing our study to a larger set of methods, with a much deeper validation phase that would include dozens of Github Java projects, for which we can submit pull requests to reduce their energy consumption by substituting the I/O methods. libraries and their read and write methods. Concretely, we assessed the energy consumption of 27 different methods using dedicated micro-benchmarks regarding several scenarios: read the whole file at once, read the file by chunks (with optimal buffer size), seek specific data within a file and write data to a file. Our experiments showed that not all read and write methods exhibit the same energy consumption while reading or writing data. On one hand, some methods can be very efficient, such as using NIO channels for read operations. On the other hand, other methods can be very inefficient, such as using Random Access File to read or write data.

To validate our results, we substituted/compared I/O methods on 4 real benchmarks and real Java projects with methods that registered a good energy efficiency with microbenchmarks. We were able to reduce the energy consumption on three of them by using NIO Channels, achieving 15%, 3%, and 30% energy savings for K-nucleotide, Zip4j and Javax.Crypto, respectively.

For future works, we will consider a massive reproduction of our results on a large set of projects. We plan on substituting the default I/O methods on multiple Github Java projects, assess the energy consumption, and open pull requests on those projects to enhance their energy efficiency. We also aim at automating a plug-in that detects energy-consuming I/O methods and recommends more energy-efficient alternatives.

Fig. 1 :Fig. 2 :

 12 Fig. 1: Energy consumption to read the whole content of files.

Fig. 3 :

 3 Fig. 3: Energy consumption of read methods for a large file.

Fig. 4 :

 4 Fig. 4: The average of read data per joule for each method.

Fig. 5 :

 5 Fig. 5: Energy consumption to read files with different buffer sizes.

Fig. 6 :

 6 Fig.6: Energy consumption to seek data from files of different sizes.

Fig. 7 :

 7 Fig. 7: Energy consumption to write files for several file sizes.

Fig. 8 :

 8 Fig. 8: Energy consumption of write methods for a large file.

Fig. 9 :

 9 Fig.9: The average of written data per joule for each method.

  VI. CONCLUSIONThis paper reports on an empirical investigation of the key differences in energy consumption of some famous Java I/O Energy consumption of Fasta to generate and write 1.5 GB of nucleotide. Energy consumption of K-nucleotide to to read the data generated by Fasta. Energy consumption of Zip4J to zip our large file with different read methods. Energy consumption of the Crypto library to decrypt a 3 GB file with different read methods.

Fig. 10 :

 10 Fig.10: Comparing the energy consumption of Fasta with multiple write methods, K-nucleotide with multiple method to read the Fasta generated file, Zip4j to read and zip our large file using different read methods, and the Crypto library to decrypt a 3 GB file.

TABLE I :

 I The list of file sizes.

	Method	File Size	Lines count	Line length
	Tiny	100 KB	1,000	100
	Small	15 MB	100,000	150
	Medium	200 MB	1,000,000	200
	Medium-large	3.2 GB	8,000,000	400
	Large	16 GB	40,000,000	400

TABLE II :

 II The list of the studied I/O classes and methods

	Class	Acronym	Method	Purpose Availability	Description
	java.io.InputStreamReader	IOSTREAM	read(Byte[])	Read	JDK 1.0	InputStreamReader is a bridge from byte streams to
						character streams. It reads bytes and decodes them
						into characters using a specified charset
	java.io.OutputStreamWriter	IOSTREAM	write(String)	Write	JDK 1.0	OutputStreamWriter is a bridge from character
						streams to byte streams: Characters written to it are
						encoded into bytes using a specified charset
	java.io.FileInputStream	IOSTREAM	Skip(long)	Seek	JDK 1.0	A FileInputStream obtains input bytes from a file in
			readAllBytes()	ReadAll		a file system. It is meant for reading streams of raw bytes such as image data
	java.io.BufferedInputStream	BIOSTREAM	readLine()	Read	JDK 1.0	A BufferedInputStream adds the ability to buffer the
						input and to support the limit, in addition to the mark
						and reset methods
	java.io.BufferedOutpuStream	BIOSTREAM	write(String)	Write	JDK 1.0	It Implements a buffered output stream. It can write
						bytes to the underlying output stream without nec-
						essarily causing a call to the underlying system for
						each byte written
	java.io.FileReader	FILEREADER	read(char[])	Read	JDK 1.1	FileReader is meant for reading streams of characters
	java.io.FileWriter	FILEWRITER	write(String,int,int)	Write	JDK 1.1	FileWriter is meant for writing streams of characters
	java.io.BufferedReader	BFILEREADER	readLine()	Read	JDK 1.1	Reads text from a character-input stream, buffering
						characters so as to provide for the efficient reading
						of characters, arrays, and lines
	java.io.BufferedWriter	BFILEWRITER	write(String)	Write	JDK 1.1	Writes text to a character-output stream, buffering
						characters so as to provide for the efficient writing
						of single characters, arrays, and strings
			read(ByteBuffer)	Read	JDK 1.4	A Filechannel is a SeekableByteChannel that is
	java.nio.channels.FileChannel	CHANNEL	write(ByteBuffer) position(long)	Write Seek		connected to a file. It has a current position within its file which can be both queried and modified
	java.nio.FileChannel	OMCHANNEL	map(MapMode,long,long)	Read	JDK 1.4	Maps a region of this channel's file directly into
						memory
			readLine()	Read	JDK 1.7	This class consists exclusively of static methods that
	java.nio.Files	NIOF	write(String) readAllLines(Path)	Write ReadAll		operate on files, directories, or other types of files. It delegates to the associated file system provider to perform the file operations
	java.util.Scanner	SCANNER	nextLine()	Read	JDK 1.5	A simple text scanner which can parse primitive
						types and strings using regular expressions
			readLine()	Read	JDK 1.0	A random access file behaves like a large array of
	java.io.RandomAccesFile	RAF	writeBytes(String) seek(long)	Write Seek		bytes stored in the file system with a pointer into the implied array, used for both read and write operations
			readFileToString(File,Charset)	Read	NA	Apache General file manipulation utilities. It offers
	apache.commons.io.FileUtils	APACHE	write(File,List¡String¿,Boolean)	Write		reading, writing, and much more operations
			readFileToByteArray(File, Charset)	ReadAll
	google.common.io.CharSource		readLine()	Read	NA	Google library that provides utility methods for
	google.common.io.CharSink	GUAVA	write(String)	Write		working with files, including reading and writing
	google.common.io.Files		readLines(file, Charset)	ReadAll		operations.
	format of the previous file, extracts the DNA sequence number
	Three, and updates a hash-table of k-nucleotide keys to count
	values within a particular reading-frame.

TABLE III :

 III Energy consumption (joules) and execution time (ms) for reading files of different sizes by chunks.

	Method	Tiny		Small	Medium	Medium-Large	Large
		Energy Time	Energy	Time	Energy	Time	Energy	Time	Energy	Time
	Apache	0.03	1.9	0.68	58	7.7	724	122	11.5 * 10 3	678	62 * 10 3
	BFileReader	0.01	1	0.69	61	7.7	714	119	11.3 * 10 3	655	60 * 10 3
	BIOStream	0.01	1	0.7	60	8.6	748	118	11.2 * 10 3	658	60 * 10 3
	Channel	0.02	1.5	0.32	27	4.4	434	83	7.9 * 10 3	476	43 * 10 3
	FileReader	0.01	1	0.43	35	5	500	90	9 * 10 3	568	50 * 10 3
	Guava	0.03	1.8	0.67	57	7.4	707	118	11.2 * 10 3	658	60 * 10 3
	IOStream	0.03	1	0.49	44	7	683	112	10.9 * 10 3	625	58 * 10 3
	NIOF	0.01	1.1	0.7	58	7.5	716	120	11.5 * 10 3	670	62 * 10 3
	OMChannel	0.01	1	0.3	25	5.1	488	83	7.9 * 10 3	NA	NA
	RAF	1.04	86	120	1200	1528	157062	24548	25 * 10 5	1.2 * 10 5	13 * 10 6
	Scanner	0.1	8	3.1	230	19.4	1697	563	5 * 10 4	2893	26 * 10 4

TABLE IV :

 IV Energy consumption (joules) and execution time (ms) for seeking data from different files.

	Method	Tiny		Small		Medium	Medium-Large		Large
		Energy Time	Energy Time	Energy Time	Energy	Time	Energy	Time
	Channel	0.01	1.2	0.07	7.6	0.5	49.5	7.7	766	42	4.1 * 10 3
	IOStream	0.01	1.1	0.06	7.6	0.6	62	8.3	814	225	8.2 * 10 3
	RAF	0.01	1.6	1.2	111	17	1693	547	5.3 * 10 4	2847	2.7 * 10 5

TABLE V :

 V Energy consumption (joules) and execution time (ms) for writing files of different sizes by chunks.

		Method	Tiny		Small	Medium	Medium-Large	Large
			Energy Time	Energy	Time	Energy	Time	Energy	Time	Energy	Time
		Apache	0.20	13.3	8.4	760	8.7	7.8 * 10 3	679	6.2 * 10 4	2526	2.3 * 10 5
		BFileWriter	0.24	15.1	9.5	794	8.6	7.8 * 10 3	6.1 * 10 4	11.3 * 10 3	2501	2.3 * 10 5
		BIOStream	0.22	13.7	8.9	770	82	7.5 * 10 3	662	6.0 * 10 4	2502	2.3 * 10 5
		Channel	0.23	14.5	10	919	99	9.1 * 10 3	798	7.3 * 10 4	3293	2.8 * 10 5
		FileWriter	0.15	10.5	8.4	764	83	7.6 * 10 3	669	6.1 * 10 4	2518	2.3 * 10 5
		Guava		1.18	80.7	95	6882	962	7.1 * 10 4	7507	5.5 * 10 5	15592	1.7 * 10 6
		IOStream	0.12	9	8.5	765	83	7.5 * 10 3	671	6.1 * 10 4	2522	2.3 * 10 5
		NIOF		0.32	22.3	27	1714	238	1.5 * 10 4	1897	1.2 * 10 5	3684	3.4 * 10 5
		RAF		0.14	10.6	10	920	98	9 * 10 3	795	7.3 * 10 4	2932	2.6 * 10 5
	Energy consumption (J)	1000 1500 2000 2500 3000 3500	Method BFileWriter BIOStream NIOF Apache RAF FileWriter IOStream Channel
		500
		0
		tiny		small	medium File size	medium-large	large

https://github.com/srikanth-lingala/zip4j

https://docs.oracle.com/javase/7/docs/api/javax/crypto/package-summary. html

ONMEMORYCHANNEL could not be used on a large file due to memory limitation, as it uses memory mapping.


Zakaria Ournani
email: zakaria.ournani@inria.fr

Mohammed Chakib Belgaid
email: chakib.belgaid@inria.fr

Romain Rouvoy
email: romain.rouvoy@univ-lille.fr

Pierre Rust
email: pierre.rust@orange.com

Joël Penhoat

Evaluating the Impact of Java Virtual Machines on Energy Consumption

Keywords:

Background. The Java Virtual Machine (JVM) platforms have known multiple evolutions along the last decades to enhance both the performance they exhibit and the features they offer. With regards to energy consumption, few studies have investigated the energy consumption of code and data structures. Yet, we keep missing an evaluation of the energy efficiency of existing JVM platforms and an identification of the configurations that minimize the energy consumption of software hosted on the JVM.

Aims. The purpose of this paper is to investigate the variations in energy consumption between different JVM distributions and parameters to help developers configure the least consuming environment for their Java application.

Method. We thus assess the energy consumption of some of the most popular and supported JVM platforms using 12 Java benchmarks that explore different performance objectives. Moreover, we investigate the impact of the different JVM parameters and configurations on the energy consumption of software.

Results. Our results show that some JVM platforms can exhibit up to 100% more energy consumption. JVM configurations can also play a substantial role to reduce the energy consumption during the software execution. Interestingly, the default configuration of the garbage collector was energy efficient in only 50% of our experiments.

Conclusion.

Finally, we provide an Open source tool, named J-Referral that recommends an energy-efficient JVM distribution and configuration for any Java application.

INTRODUCTION

Software services are widely deployed to support our daily activities, being mobile or hosted in the cloud. Yet, beyond this undeniable success, the environmental impact of Information and Communication Technology (ICT) is raising concerns and calls for solutions to reduce the energy footprint of software services [START_REF]Lean ICT -Towards Digital Sobriety[END_REF].

Software developers often report that such solutions should come from more energy-efficient hardware components or optimized algorithms [START_REF] Ournani | Taming Energy Consumption Variations In Systems Benchmarking[END_REF][START_REF] Wang | Potential Effects on Server Power Metering and Modeling[END_REF] but, given the complexity of modern software environments, the composition of software layers makes this sustainability objective particularly challenging.

Given this context, this paper more specifically investigates the impact of one of these layers, the runtime environment and its settings, on the energy consumption of a hosted software service. More precisely, we aim at revealing the importance of carefully selecting and configuring the Java Virtual Machine (JVM) to reduce the energy consumption of any software service built from any language compatible with the Java ecosystem (Java, Kotlin, Scala, Groovy, Clojure, Jython, etc.).

The empirical study we conduct in this paper reports on the energy footprint of several versions of popular JVM distributions that are freely available for download. Beyond the choice of an appropriate runtime and its most energy-efficient version, we also consider the impact of exposed JVM settings to maximize the energy savings for a given software service. The observations of this study aim to quantify the role played by internal JVM mechanisms, like the Just in Time (JIT) compiler and the Garbage Collector (GC), in the reduction of the energy consumption of hosted applications. More formally, we formulate the following research questions:

RQ 1: What is the impact of existing JVM distributions on the energy consumption of Java-based software services? RQ 2: What are the relevant JVM settings that can reduce the energy consumption of a given software service? This paper comes with a set of contributions that can be summarized as:

(a) Reporting on the energy-efficiency of a large panel of JVM when running acknowledged benchmarks, (b) Identifying and assessing the key JVM settings that can influence the energy consumption of a software service, (c) Sharing guidelines and prerequisites that will help in configuring the most energy-efficiency environment before deployment, (d) Providing a JVM benchmarking environment to evaluate the energy-efficiency of upcoming JVM distributions and their settings, (e) Delivering an open-source software (OSS) tool, named J-Referral, to recommend the most energy efficient JVM distribution among 85 JVMs/versions and hundreds of configurations for Java applications. The remainder of this paper is organized as follows. Section 3 introduces the experimental protocol and methodology (hardware, projects, tools, and methodology) we adopted in this study. Section 4 analyzes the results of our experiments on the energy consumption of the different JVM configurations. Section 5 discusses the related works to reduce the energy consumption of Java-based Software Services. Finally, Section 6 covers our conclusions.

THE JAVA VIRTUAL MACHINE

Java was originally developed by James Gosling at Sun Microsystems and released in 1995, before being acquired by Oracle in 2010.

One key design goal of Java is portability, which means that Java applications must run similarly on any combination of hardware and operating system with adequate runtime support. This is achieved by compiling the Java language code to an intermediate representation, called Java bytecode, instead of machine code. Java bytecode instructions are analogous to machine code, but executed by a Java Virtual Machine (JVM), which is specific to the host machine. For example, Oracle keeps offering the HotSpot JVM, while the official reference implementation is now the OpenJDK JVM-a free and open-source software used by most developers.

Programs written in Java have a reputation for being slower and requiring more memory than those written in C++, but Just-in-Time (JIT) compilation, embedded in the JVM, delivers a boost of performance by opportunistically compiling bytecode to machine code at runtime. The JIT combines two compilers, C1 and C2 (also known as Client & Server VM), which are triggered based on the activity of the hosted application. Additionally, Java uses an automatic garbage collector (GC) to manage memory in the object lifecycle and recovering the memory once objects are no longer in use. Each JVM usually includes multiple GC, each designed to satisfy different requirements.

By default, HotSpot uses both C1 and C2 as tiered compilers, * and the Garbage-First (G1) GC with a maximum number of GC threads limited by available CPU resources and heap size, whose initial size is set 1/64 t h of physical memory and maximum size may reach up to 1/4 t h of physical memory. † At the time of writing this paper, the latest JVM version is Java 15, released in September 2020, while Java 11 is the current Long-Term Support (LTS) version. As Java 9, 10, 12, and 13 are no longer supported, Oracle advises developers to immediately transition to the latest version (currently Java 15), or an LTS release.

Beyond HotSpot, one can observe that the initial JVM design leads to numerous initiatives to improve the performances of Java applications, including new hotswapping strategies-with the Dynamic Code Evolution Virtual Machine (DCE VM) [START_REF] Würthinger | Unrestricted and safe dynamic code evolution for Java[END_REF]-or alternative JIT-with GraalVM. ‡ This also includes alternative implementations, like IBM J9 JVM, which is currently distributed as part of the Eclipse foundation, and known as J9. § Given the wide diversity of distributions and related settings, this paper aims to study the impact of the features implemented by available JVM distributions on the energy consumption of the hosted Java software services.

EXPERIMENTAL PROTOCOL

To investigate the effect that could have the JVM distribution choice and/or parameters on software energy consumption, we conducted a wide set of experiments on a cluster of machines and using several established Java benchmarks and JVM configurations. Hardware Settings. To report on reproducible measurements, we used the cluster Dahu of the G5K platform [START_REF] Balouek | Adding Virtualization Capabilities to the Grid'5000 Testbed[END_REF] for most of our experiments. This cluster is composed of 32 identical compute nodes, which are equipped with 2 Intel Xeon Gold 6130 and 192 GB of RAM. Our experimental protocol enforces that the software under test is the only process executed on the node configured with a very minimal Linux Debian 9 (4.9.0 kernel version). The minimal OS configuration ensures that only mandatory services and daemons are kept active to conduct robust experiments and reduce the factors that can affect the energy consumption measurements during our experiments [START_REF] Ournani | Taming Energy Consumption Variations In Systems Benchmarking[END_REF].

Energy Measurements. We used Intel RAPL as a physical power meter to analyze the energy consumption of the CPU package and the DRAM. RAPL is one of the most accurate tools to report on the global energy consumption of a processor [START_REF] Desrochers | A Validation of DRAM RAPL Power Measurements[END_REF][START_REF] Kashif Nizam Khan | RAPL in Action: Experiences in Using RAPL for Power Measurements[END_REF]. We note that, due to CPU energy consumption variations issues [START_REF] Ournani | Taming Energy Consumption Variations In Systems Benchmarking[END_REF], we used the same node for all our experiments. Moreover, we tried to be very careful, while running our experiments, not to fall in most common benchmarking "crimes" [START_REF] Van Der Kouwe | Benchmarking Crimes: An Emerging Threat in Systems Security[END_REF]. Every single experiment, therefore, reports on energy metrics obtained from at least 20 executions of 50 iterations per benchmark. All of our experiments are available for use/reproducibility from our anonymous repository.

Java Virtual Machines. We considered a set of 52 JVM distributions taken from 8 different providers/packagers mostly obtained from SDKMAN, ∥ as listed in Table 1. Depending on providers, either all the versions, majors, or LTS are made available by SDKMAN.

Java Benchmarks. We ran our experiments across 12 Java benchmarks we picked from OpenBenchmarking.org. * * This includes 5 acknowledged benchmarks from the Dacapo benchmark suite v. 9.12 [START_REF] Blackburn | The DaCapo Benchmarks: Java Benchmarking Development and Analysis[END_REF], namely Avrora, H2, Lusearch, Sunflow and PMD, that have been widely used in previous studies and proven to be accurate for memory management and computer architecture communities [START_REF] Kalibera | A black-box approach to understanding concurrency in DaCapo[END_REF][START_REF] Lengauer | A Comprehensive Java Benchmark Study on Memory and Garbage Collection Behavior of DaCapo, DaCapo Scala, and SPECjvm[END_REF]. It consists of open-source and real-world applications with non-trivial memory loads. Then, we also considered 7 additional benchmarks from the Renaissance benchmark suite [START_REF] Prokopec | Renaissance: Benchmarking Suite for Parallel Applications on the JVM[END_REF][START_REF] Prokopec | Renaissance: benchmarking suite for parallel applications on the JVM[END_REF], namely ALS, Dotty, Fj-kmeans, Neo4j, Philosophers, Reaction and Scrabble, which offers a diversified set of benchmarks aimed at testing JIT, GC, profilers, analyzers, and other tools. The benchmarks we picked from both suites exercise a broad range of programming paradigms, including concurrent, parallel, functional, and objectoriented programming. 1 first depicts the accumulated energy consumption of the 12 Java benchmarks per JVM distribution and major versions (or LTS when unavailable). Concretely, We measure the energy consumption of each of the benchmarks and compute the ratio of energy consumption compared to HotSpot-8, which we consider as the baseline in this experiment. Then, we sum the ratios of the 12 benchmarks and depict them as percentages in Figure 1.

One can observe that, along with time and versions, the energy efficiency of JVM distributions tends to improve (10% savings), thus demonstrating the benefits of optimizations delivered by the communities. Yet, one can also observe that energy consumption may differ from one distribution to another, thus showing that the choice of a JVM distribution may have a substantial impact on the energy consumption of the deployed software services. For example, one can note that J9 can exhibit up to 15% of energy consumption overhead, while other distributions seem to converge towards a lower energy footprint for the latest version of Java. As GraalVM adopts a different strategy focused on LTS support, one can observe that its recent releases provide the best energy efficiency for Java 11, but recent releases of other distributions seem to reach similar efficiency for Java 13 and above, which are recent versions not supported by GraalVM yet.

Interestingly, this convergence of distributions has been observed since Java 11 and coincides with the adoption of DCE VM by HotSpot. Ultimately, 3 clusters of JVMs that encompass JVMs with similar energy consumption can be seen through Figure 1: J9, the HotSpot and its variants, and GraalVM. Additional detailed figures to illustrate the evolution of energy consumption per benchmark/JVM are made available from the anonymous repository. † † † † https://anonymous.4open.science/r/jvm-comparaison-213E/Readme.md Then, Figure 2 depicts the evolution of the energy consumption of the 12 benchmarks, when executed on the HotSpot JVM. Figure 2 reports on the energy consumption variation of individual benchmarks, using to HotSpot-8 as the baseline. Our results show that the JVM version can severely impact the energy consumption of the application. However, unlike Figure 1, one can observe that, depending on applications, latest JVM versions can consume less energy (60% less energy for Scrabble) or more energy (25% more energy for the Neo4J). It is worth noticing that the energy consumption of some benchmarks, such as Reactors, exhibit large variations across JVM versions due to experimental features and changes that are not always kept when releasing LTS versions (version 11 here). For example, the introduction of VarHandle to allow low-level access to the memory order modes available in JDK 9 and work along Unsafe Classe that was removed from from JVM 11. ‡ ‡ Given that the wide set of distributions and versions seems to highlight 3 classes of energy behaviors, the remainder of this paper considers the following distributions as relevant samples of JVM to be further evaluated: 20.2.0.r11-grl (GraalVM), 15.0.1-open (HotSpot-15), 15.0.21.j9 (J9). We also decided to keep the 8.0.275open (HotSpot-8) as a baseline JVM for some figures to highlight the evolution of energy consumption over time/versions. Figure 3 further explores the comparison of energy efficiency of the JVM distributions per benchmark. One can observe that, depending on the benchmark's focus, the energy efficiency of JVM distributions may strongly vary. When considering individual benchmarks, J9 performs the worst for at least 6 out of 12 benchmarks-i.e., worst ratio among the 4 tested distributions. Even though, J9 can still exhibit a significant energy saving for some benchmarks, such as Avrora, where it consumes 38% less energy than HotSpot and others.

Interestingly, GraalVM delivers good results overall, being among the distributions with a low energy consumption for all benchmarks, except for Reactors and Avrora. Yet, some differences still can be observed with HotSpot depending on applications. The newer version of HotSpot-15 was averagely good and, compared to HotSpot-8, it significantly enhances energy consumption for most scenarios. ‡ ‡ https://blogs.oracle.com/javamagazine/the-unsafe-class-unsafe-at-any-speed Finally, Neo4J is the only selected benchmark where HotSpot-8 is more energy efficient than HotSpot-15.

Service-oriented applications. In this section, instead of considering bounded execution of benchmarks, we run the same benchmarks as services for 20 minutes, and we compare the average power and total requests processed by each of the 3 JVM distributions. Globally, the results showed that the average power when using GraalVM, HotSpot, and OpenJ9 is often equivalent and stable over time. This means that the energy efficiency observed for some JVM distributions with Job-oriented applications is mainly related to shorter execution times, which incidentally results in energy savings. Nonetheless, we can highlight two interesting observations for two benchmarks whose behaviors differ from others. First, the analysis of the Scrabble benchmark experiments showed that, in some scenarios, some JVMs can exhibit different power consumptions. Figure 4 depicts the power consumed by the 3 JVM distributions for the Scrabble benchmark. One can clearly see that GraalVM requires average power of 109 W, which is 9 W higher than HotSpot-15 and 15 W higher than J9. When it comes to the number of requests processed by Scrabbles during that same amount of time, GraalVM completes 5,336 requests, against 3,595 for HotSpot and 2,603 for J9, as shown in Table 3. The higher power usage for GraalVM helped in achieving a high amount of requests, but also the fastest execution of every, request which was 40% faster on GraalVM. Thus, GraalVM was more energy efficient, even if it uses more power, which confirms the results observed in Figure 3 for this benchmark. The second interesting situation was observed on the Dotty benchmark. More specifically, during the first 100 seconds of the execution of the Dotty benchmark on all evaluated JVMs. At the beginning of the execution, GraalVM has a slightly lower power consumption, is faster, and consumes 10% less energy. After about 150 seconds, the power differences between the 3 JVMs is barely noticeable. One can, however, notice the effect of the JIT, as HotSpot takes the advantage over GraalVM and becomes more energy efficient. In total, HotSpot completes 597 requests against 510 for GraalVM and 381 for J9, as shown in Table 3. HotSpot was thus the best choice on the long term, which explains why it is always necessary to consider a warm-up phase and wait for the JIT to be triggered before evaluating the effect of the JVM or the performance of an application. This is exactly what we did in our experiments, and why HotSpot was more energy efficient than GraalVM in Figure 3, thus ignoring the warm-up phase would have been misleading.

To answer RQ 1, we conclude that-while most of the JVM platforms perform similarly-we can cluster JVMs in 3 classes: HotSpot, J9, and GraalVM. The choice of one JVM of these classes can have a major impact on software energy consumption, that strongly depends on the application context. When it comes to the JVM version, latest releases tend to offer the lowest power consumption, but experimental features should be carefully configured, thus further questioning the impact of JVM parameters.

Energy Impact of JVM Settings

The purpose of our study is not only to investigate the impact of the JVM platform on the energy consumption, but also the different JVM parameters and configurations that might have a positive or negative effect, with a focus on 3 available settings: multi-threading, JIT, and GC.

4.2.1

Multithreading. The purpose behind this phase is to investigate the impact JVM thread management strategies on the energy consumption. This encompasses exploring if the management strategies of application-level parallelism (so called threads) results in different energy efficiencies, depending on JVM distributions.

Investigating such an hypothesis requires a selection of highly parallel and CPU-intensive benchmarks, which is one of the main criteria for our benchmark selection. As no tool can accurately monitor the energy consumption at a thread level, we monitor the global power consumption and CPU utilization during the execution using RAPL for the energy, and several Linux tools for the CPU-utilization (htop, cpufreq). Knowing that most of the benchmarks are multithreaded jobs that use multiple cores, further analysis of thread management is required to understand the results of our previous experiments. We thus selected the benchmarks that highlighted the highest differences along JVM distributions from Figure 3, namely Avrora and Reactors. We studied their multi-threaded behavior to optimize their energy efficiency.

Figures 6 and7 deliver a closer look to the thread allocation strategies adopted by JVM. First, Figure 6 illustrates the active threads count evolution over time (excluding the JVM-related threads, usually 1 or 2 extra threads depending on the execution phase) for Avrora. One can notice through the figure that J9 exploits the CPU more intensively by running much more parallel threads compared to other JVMs (an average of 5.1 threads per second for J9 while the other JVMs do not exceed 1.5 thread per second). Furthermore, the number of context switches is twice as big for J9, while the number of soft page faults is twice as small. The efficient J9 thread management explains why running the Avrora benchmark took much less time and consumed less energy, given that no other difference for the JIT or GC configuration was spotted between the JVMs. Another key reasons of the J9's efficiency for the Avrora benchmark is memory allocation, as OpenJ9 adopts a different policy for the heap allocation. It creates a non-collectable thread local heap (TLH) within the main heap for each active thread. The benefit of cloning a dedicated TLH is the fast memory access for independent threads: each thread has its own heap and no deadlock can occur.

The second example in Figure 7 depicts the active threads evolution over time of the Reactors benchmark. In this case, all the JVMs have a close average of threads per second. Nevertheless, one can still observe that HotSpot-15 and J9 keep running faster, which confirms the results of Figure 3, where both JVMs consume much less energy compared to GraalVM and HotSpot-8. This difference in energy consumption between benchmarks can be less likely caused by thread management for the Reactors benchmark, as HotSpot-8 reports on a higher average of active threads. However, the TLH mechanism was not as efficient as for the Avrora benchmark, as dedicating a heap for each thread can also cause some extra memory usage for data duplication and synchronization, especially if a lot of data is shared between threads.

In conclusion, JVMs thread management can sometimes constitute a key factor that impacts software energy consumption. However, we suggest to check and compare JVMs before deploying a software, especially if the target application is parallel and multithreaded.

Just-in-Time Compilation. The purpose of experiments on

JIT is to highlight the different strategies that can impact software energy consumption within a JVM and between JVMs. We identified a set of JIT compiler parameters for every JVM platform. For J9, we considered fixing the intensity of the JIT compiler at multiple levels (cold, warm, hot, veryhot, and scorching). § § The hotter the JIT, the more code optimization to be triggered. We also varied the minimum count method calls before a JIT compilation occurs (10, 50, 100), and the number of JIT instances threads (from 1 to 7). For Hotspot-15, we conducted experiments while disabling the tiered complication (that generates compiled versions of methods that collect profiling information about themselves), and we also varied the JIT maximum compilation level from 0 to 4, we also tried out HotSpot with a basic GraalVM JIT. We note that the level 0 of JIT compilation only uses the interpreter, with no real JIT compilation. Levels 1, 2, and 3 use the C1 compiler (called client-side) with different amount of extra tuning. The JIT C2 (also called server-side JIT) compiler only kicks-in at level 4.

For GraalVM, we conducted experiments with and without the JVMCI (a Java-based JVM compiler interface enabling a compiler written in Java to be used by the JVM as a dynamic compiler). We also considered both the community and economy configurations (no enterprise). A JIT+AOT (Ahead Of Time) disabling experiment has also been considered for all of the 3 JVM platforms. Table 4 reports on the energy consumption of the experiments we conducted for most of the benchmarks and JIT configurations under study.

The p-values are computed with the mann-whitney test, with a null hypothesis of the energy consumption being equal to the default configuration. The p-values in bold show the values that are significantly different from the default configuration with a 95% confidence, where the values in green highlight the strategies that consumed significantly less energy than default (less energy and significant p-value).

For J9, we noticed that adopting the default JIT configuration is always better than specifying a custom JIT intensity. The warm configuration delivers the closest results to the best results observed with the default configuration. Moreover, choosing a low minimum count of method calls seems to have a negative effect on the execution time and the energy consumption. The only parameter that can give better performance than the default configuration in some § § [https://www.eclipse.org/openj9/docs/jit/] cases is the number of parallel JIT threads-using 3 and 7 parallel threads-but is not statistically significant.

For GraalVM, the default community configuration is often the one that consumes the least energy. Disabling the JVMCI can-in some cases-have a benefit (16% of energy consumption reduction for the H2 benchmark), but still gave overall worst results (80% more energy consumption for the Neo4J benchmark). In addition, switching the economy version of the GraalVM JIT often results in consuming more energy and delaying the execution.

For HotSpot, keeping the default configuration of the JIT is also mostly good. In fact, the usage of the C2 JIT is often beneficial (JIT level 4) in most cases, while using the GraalVM JIT reported similar energy efficiency. Yet, some benchmarks showed that using only the C1 JIT (JIT level 1) is more efficient and even outperforms the usage of the C2 compiler. 10% on Avrora and 30% on Pmd are examples of energy savings observed by using the C1 compiler. However, being limited to the C1 compiler can also cause a huge degradation in energy consumption, such as 32% and 34% of additional energy consumed for the Dotty and FJ-kmeans benchmarks, respectively. Hence, if it is a matter of not using the C2 JIT, the experiments have shown that the level 1 JIT is always the best, compared to levels 2 or 3 that also use the C1 JIT, but with more options, such as code profiling that impacts negatively the performance and the energy efficiency. Level 0 JIT compilation should never be an option to consider. No p-value has been computed for Level 0, due to the limited amount of iterations executed with this mode (very high execution time, clearly much more consumed energy).

Globally, we conclude through these experiments that keeping the default JIT configuration was more energy efficient in 80% of our experiments and for the 3 classes of JVMs. This advocates that using the default JIT configuration that can often deliver near-optimal energy efficiency. Although, some other configurations, such as using only the C1 JIT or disabling the JVMCI could be advantageous in some cases.

Garbage Collection.

Changing or tuning the GC strategy has been acknowledged to impact the JVM performances [START_REF] Libič | On the Limits of Modeling Generational Garbage Collector Performance[END_REF]. To investigate if this impact also benefits to energy consumption, we conducted a set of experiments on the selected JVMs. We considered different garbage collector strategies with a limited memory quantity of 2 GB, and recorded the execution time and the energy consumption. The tested GC strategies options mainly vary between J9 and the other 2 JVMs, as detailed in Table 5.

For HotSpot and GraalVM, we also considered many GC policies, as described in Table 6. Furthermore, other GC settings have also been tested for all JVM platforms, such as the pause time, the number of parallel threads and concurrent threads and tenure age.

Table 7 summarizes the results of all the tested GC strategies with our selected benchmarks and the p-values of the mann-whitney test, with a null hypothesis of the energy consumption being equal to the default configuration with a 95% confidence. The p-values in bold show the values that are significantly different from the default configuration, where the values in green highlight the strategies that consumed significantly less energy than default. For GraalVM, one can see that the GC default configuration is efficient in most experiments, compared to other strategies. The main noticeable impact is related to the ParallelGC and ParallelOldGC. In fact, the ParallelGC Known as throughput collector: similar to SerialGC, but uses multiple threads to speed up garbage collections for scavenges parallelOldGC Use parallel garbage collection for the full collections, enabling it automatically enables the ParallelGC can be 13% more energy efficient in some applications with a significant p-value, such as Reactors, compared to default. However, the same GC strategy can cause the software to consume twice times more, as for the Neo4j benchmark, due to the high communications between the GC threads, and the fragmentation of the memory. For J9, the default Gencon GC causes the software to report an overall good energy efficiency among the tested benchmarks. However, other GC can cause better or worse energy consumption than Gencon depending on workloads. Using the Metronome GC consumes 35% less energy for the ALS benchmark and 17% less energy for the Sunflow benchmark, but it also consumes twice energy for the Neo4j benchmark and 28% more energy for Reactors. The reason is that Metronome occurs in small preemptible steps to reduce the GC cycles composed of many GC quanta. This suits well for real-time applications and can be very beneficial when long GC pauses are not desired, as observed for ALS. However, if the heap space is insufficient after a GC cycle, another cycle will be triggered with the same ID. As Metronome supports class unloading in the standard way, there might be pause time outliers during GC activities, inducing a negative impact on the Neo4j execution time and energy consumption.

The same goes for the Balanced GC that tries to reduce the maximum pause time on the heap by dividing it into individually managed regions. The Balanced strategy is preferred to reduce the pause times that are caused by global GC, but can also be disadvantageous due to the separate management of the heap regions, such as for ALS where it consumed about three times the energy consumption, compared to the default Gencon GC. On the other hand, the Optthruput GC, which stops the application longer and less frequently, gave very good overall results and sometimes even outperformed the Gencon GC by a small margin. Other JVM parameters, such as the Concur-rentScavenge or noAdaptiveTenure did not have a substantial impact during our experiments. Finally, the results of HotSpot shared similarities with GraalVM. The ParallelGC happened to give better (6% for Dotty) or worst (10% for Neo4j) energy efficiency compared to the default GC. On the other hand, ParallelOldGC and Serial GC gave better results than the default G1 GC. More specifically, the second one consumed 30% and 6% less energy than default GC for the Neo4j and Dotty benchmarks, respectively. The most interesting result for HotSpot is the 30% energy reduction obtained with the Serial GC. This last was also more efficient on ALS (6% less energy), compared to the default G1 GC, due to its single-threaded GC that only uses one CPU core.

Therefore, we noticed during our experiments that, even if using the default GC configuration ensures an overall steady and correct energy consumption, we still found other settings that reduce that energy consumption in 50% of our experiments. Tuning the GC according to the hosted app/benchmark is thus critical to reduce the energy consumption.

To answer RQ 2, we conclude that users should be careful while choosing and configuring the garbage collector as substantial energy enhancements can be recorded from a configuration to another. The default GC consumed more energy than other strategies in most of the situations. However, keeping the default JIT parameters often delivers near-optimal energy efficiency. In addition, the JVM platforms can handle differently multi-threaded applications and thus consume a different amount of time/energy. Dedicated performance tuning evaluations should therefore be conducted on such software to identify the most energy-efficient platform and settings.

J-Referral.

To help developers and practitioners choosing an energy efficient JVM distribution and configuration, we propose a tool named J-Referral. This tool takes a Java application as an input and a launch script to tune the JVM configuration. The recommendation is is based on the 85 JVM distributions and versions made available by SDKMAN. Beyond the JVM distribution and version recommendation, J-Referral automatically explores many GC and JIT options for each distribution. Hundreds of combinations are thus compared to recommend an energy efficient JVM distribution and the associated configuration parameters. Table 8 illustrates an example of the final report of J-Referral. The tool was tested for 2 real Java projects: Zip4J and and Knucleotide. * * * . Zip4J runs a large file compression, while K-nucleotide extracts a DNA sequence, and updates a hashtable of k-nucleotide keys to count specific values. The short report presented in Table 8 shows the ratio of potential energy saving between the most and least energy consuming tested JVM (40% and 70% energy savings for Zip4J and K-nucleotide respectively). Options are available for J-Referral to obtain much more detailed reports including execution time, DRAM usage, split DRAM vs. CPU consumption, etc. The tool is available as open-source software (OSS) from our anonymous repository. † † †

RELATED WORK

In this section, we review the state of the art of studies related to JVM energy consumption.

At code level. Many works investigated software energy consumption efficiency through source code changes and optimizations. For https://github.com/srikanth-lingala/zip4j * * * https://benchmarksgame-team.pages.debian.net/benchmarksgame/ performance/knucleotide.html † † † https://anonymous.4open.science/r/jreferral/Readme.md example, [START_REF] Fernandes | Assisting Non-Specialist Developers to Build Energy-Efficient Software[END_REF][START_REF] Pinto | A Comprehensive Study on the Energy Efficiency of Java's Thread-Safe Collections[END_REF] studied the effect of Java collections on energy consumption, with regards to the collection size and/or the most executed tasks on the collection (insertion, removal, search). In [START_REF] Hasan | Energy Profiles of Java Collections Classes[END_REF], Hasan et al. compared the energy consumption of several Java data structures, analyzing the bytecode using the Wala framework ‡ ‡ ‡ and assessing the evolution of the energy consumption in different scenarios (insertion at the beginning, iteration, etc.). They also used some automated replacement of LinkedList and ArrayList to simulate best-and worst-case energy consumption scenarios on real production applications. Their study showed that using inappropriate collection can cause an energy consumption inefficiency of 300% for the worst-case scenario. SEEDS and SEEDS-API is a fully automated framework, proposed in [START_REF] Manotas | SEEDS: A Software Engineer's Energy-Optimization Decision Support Framework[END_REF], to analyze source code (at bytecode level for Java) and autotunes apps to have reduce their energy consumption, with a focus on Java collection tuning. The authors reported on an improvement up to 17%. In [START_REF] Pereira | Helping Programmers Improve the Energy Efficiency of Source Code[END_REF], the same authors presented SPELL, the energy leaks detector tool. The tool uses JRapl [START_REF] Alves | Experiments on Model-Based Software Energy Consumption Analysis[END_REF][START_REF] Pinto | A Comprehensive Study on the Energy Efficiency of Java's Thread-Safe Collections[END_REF] to detect energy-inefficient code fragments by using a statistical spectrum-based energy red spots localization. Their evaluation reported up to 18% energy savings on Java applications.

At JVM level. In [START_REF] Pereira | Energy Efficiency across Programming Languages: How Do Energy, Time, and Memory Relate[END_REF], Pereira et al. investigated several programming languages, including JVM-based languages like Java and Scala, and compared them on different dimensions (energy, memory, and time) using CLBG benchmarks. Their observation reported that compiled languages are more energy-efficient than interpreted ones, overall. More advanced results expose how the selected languages may satisfy one, two, or three dimensions of the equation. The authors of [START_REF] Oi | Power-performance analysis of JVM implementations[END_REF] conducted a performance analysis and comparison between HotSpot and J9. They claimed in their results that the relative performance of HotSpot ranges from 44% to 289% of J9, while the dynamic power consumption varies from 2.7W to 7.2W using the SPECjvm2008 benchmarks.

The difference between HotSpot and J9 was also reported in other studies. Chiba et al. [START_REF] Chiba | Towards Selecting Best Combination of SQL-on-Hadoop Systems and JVMs[END_REF] evaluated the effect that could have those 2 JVM platforms on the performance of a combination of big data query engines (Spark and Tez) using TPC-DS benchmark. They reported on a 3-fold drawback that can exhibit one JVM compared to the other.

On another note, the authors of [START_REF] Lafond | An Energy Consumption Model for an Embedded Java Virtual Machine[END_REF] attempted to assess and assign an energy cost to atomic bytecode instructions together with a constant overhead of using the JVM. Their claims are rather surprising, as the energy cost should be impacted by the execution environment, workload, core frequencies, and can hardly be fixed. A similar idea was used in [START_REF] Edward | Search-Based Energy Optimization of Some Ubiquitous Algorithms[END_REF] to design a model for JVM-based software energy consumption, using a bytecode-level model. The authors described their tool, named OPACITOR, as being deterministic, accurate, and ‡ ‡ ‡ http://wala.sourceforge.net/wiki/index.php robust to the surrounding noise. However, they disable the JIT in their experiments to maintain the deterministic behavior of their tool, which does not reflect a real software execution given all the optimizations that the JVM triggers to optimize the performances.

CONCLUSION

This paper reports on an empirical investigation of the key differences in energy consumption that some of the most famous and supported JVM platforms can exhibit, in addition to the key settings that can impact this energy consumption positively or negatively. During our experiments, we considered a total of 12 well-known and diversified-purposes Java benchmarks together with a total of 52 JVMs, including many versions of 11 different distributions. The results of our investigations showed that many JVMs share energy efficiencies and can grouped into 3 classes: HotSpot, J9, and GraalVM. The 3 selected JVM classes can however report a different energy efficiency for different software and/or workloads, sometimes by a large margin. While we did not observed a unique champion when it comes to energy consumption, GraalVM reported the best energy efficiency for a majority of benchmarks. Nonetheless, each JVM can achieve the best or the worst depending on the hosted application. One cause can be thread management strategies, as observed with J9 when advantageously running Avrora. Moreover, some JVM settings can cause energy consumption variations. Our experiments showed that the default JIT compiler of the JVM is often near-optimal, in at least 80% of our experiments. The default GC, however, was outperforming alternative strategies in half of our experiments, with some large gains observed when using some alternative GC depending on the application characteristics.

To ease the integration of the above guidelines, we propose a tool, named J-Referral, to recommend the most energy-efficient JVM distribution and configuration among more than a hundred considered possibilities. It establishes a full report on the energy consumption of both CPU and DRAM components for each JVM distribution and/or configuration to help the user to choose the one with the least consumption for a Java Software.

Figure 1 :

 1 Figure 1: Energy consumption evolution of selected JVM distributions along versions.

Figure 2 :

 2 Figure 2: Energy consumption of the HotSpot JVM along versions.

Figure 3 :

 3 Figure 3: Energy consumption comparison across Java benchmarks for HotSpot, GraalVM & J9.

Figure 4 :Figure 5 :Figure 6 :

 456 Figure 4: Power consumption of Scrabble as a service for HotSpot, GraalVM & J9.

Figure 7 :

 7 Figure 7: Active threads of Reactors when using HotSpot, GraalVM, or J9.

Table 1 :

 1 List of selected JVM distributions.

	Distribution	Provider	Support Selected versions
	HotSpot	Adopt OpenJDK	All	8.0.275, 11.0.9, 12.0.2, 13.0.2, 14.0.2, 15.0.1
	HotSpot	Oracle	All	8.0.265, 9.0.4, 10.0.2, 11.0.2, 12.0.2, 13.0.2, 14.0.2, 15.0.1, 16.ea.24
	Zulu	Azul Systems	All	8.0.272, 9.0.7, 10.0.2, 11.0.9, 12.0.2, 13.0.5, 14.0.2, 15.0.1
	SapMachine	SAP	All	11.0.9, 12.0.2, 13.0.2, 14.0.2, 15.0.1
	Librca	BellSoft	All	8.0.275, 11.0.9, 12.0.2, 13.0.2, 14.0.2, 15.0.1
	Corretto	Amazon	Mjr	8.0.275, 11.0.9, 15.0.1
	HotSpot	Trava OpenJDK	LTS	8.0.232, 11.0.9
	Dragonwell	Alibaba	LTS	8.0.272, 11.0.8
	OpenJ9	Eclipse	All	8.0.275, 11.0.9, 12.0.2, 13.0.2, 14.0.2, 15.0.1
	GraalVM	Oracle	LTS	19.3.4.r8, 19.3.4.r11, 20.2.0.r8, 20.2.0.r11
	Mandrel	Redhat	LTS	20.2.0.0

Table 2

 2

	summarizes the selected benchmarks
	with a short description.
	https://anonymous.4open.science/r/jvm-comparaison-213E/Readme.md
	∥ https://sdkman.io/
	* * https://openbenchmarking.org

Table 2 :

 2 List of selected open-source Java benchmarks taken from Dacapo and Renaissance.

	Benchmark Description

Table 3 :

 3 Power per request for HotSpot, GraalVM & J9.

	Benchmark	JVM	Power (P) Requests (R) P /R ×10 -3
		GraalVM	109 W	5,336 req	20 mW
	Scrabble	HotSpot	98 W	3,595 req	27 mW
		J9	92 W	2,603 req	35 mW
		GraalVM	45 W	510 req	88 mW
	Dotty	HotSpot	45 W	597 req	75 mW
		J9	46 W	381 req	120 mW

Table 4 :

 4 Energy consumption when tuning JIT settings on HotSpot, GraalVM & J9

	JVM	Mode	ALS		Avrora	Dotty	Fj-kmeans	H2		Neo4j			Pmd	Reactors	Scrabble	Sunflow
		Default	2848 p-values	3861 p-values	2271 p-values	948 p-values	1959 p-values	3313 p-values	297 p-values	23452 p-values	452 p-values	335 p-values
	GraalVM	DisableJVMCI	3099	0.001	4012	0.041	2694	0.001	934	0.011 1771	0.005	5086	0.001	353	0.001	25007	0.007	503	0.002	354	0.227
		Economy	4503	0.001	3895	0.793	3466	0.001	1306	0.002	2560	0.001	9525	0.001 270	0.001	30317	0.001	649	0.002	392	0.002
		Default	3792 p-values	2122 p-values	3515 p-values	1271 p-values	2426 p-values	4336 p-values	277 p-values	12705 p-values	734 p-values	476 p-values
		Thread 1	4157	0.001	2121	0.875	4749	0.001	1297	0.097	2597	0.066	4906	0.001	350	0.001	12800	0.713	948	0.002	626	0.005
		Thread 3	3849	0.018	2105	0.713	3574	0.104	1259	0.371	2450	0.637	4477	0.005	294	0.004	12647	0.875	795	0.021	457	0.27
		Thread 7	3843	0.041	2386	0.372	3511	0.875	1259	0.25	2424	0.637	4431	0.104	273	0.372	12600	0.875	808	0.055	463	0.372
		Count 0	8461	0.001	2425	0.001	4877	0.001	2289	0.002	3212	0.001	10565	0.001	744	0.001	18084	0.001 1476	0.002	922	0.001
	J9	Count 1 Count 10	4281 3980	0.001 0.001	2150 2431	0.431 0.713	3164 3771	0.001 0.001	1841 1312	0.002 0.011	2546 2779	0.431 0.003	7166 4979	0.001 0.001	272 299	0.128 0.001	14715 12000	0.001 1005 0.104 860	0.002 0.005	514 1182	0.052 0.001
		Count 100	3878	0.007	2141	0.713	3469	0.227	1363	0.523	2513	0.128	4547	0.001	262	0.031	12313	0.024	768	0.16	634	0.004
		Cold	6788	0.001	2134	0.637	4855	0.001	1636	0.002	2873	0.001	7250	0.001	275	0.372	20380	0.001	870	0.005	386	0.001
		Warm	4594	0.001	2112	0.713	4253	0.001	1244	0.055	2521	0.128	5305	0.001	411	0.001	13726	0.001	913	0.002	336	0.001
		Hot	7553	0.001	2310	0.001 12749	0.001	1452	0.002	3973	0.001	8979	0.001	857	0.001	36534	0.001 1180	0.002	506	0.128
		VeryHot	15113	0.001	3300	0.001 18235	0.001	2430	0.002	7205	0.001	19359	0.001	793	0.001	38303	0.001 5420	0.002	1692	0.001
		Schorching	18316	0.001	3541	0.001 21686	0.001	2514	0.002	7855	0.001	26409	0.014	808	0.001	43929	0.001 5583	0.002	1778	0.001
		Default	2997 p-values	4014 p-values	2516 p-values	934 p-values	1796 p-values	4787 p-values	323 p-values	11685 p-values	530 p-values	325 p-values
		Graal	2999	0.637	3971	0.318	2512	0.318	929	0.609 1662	0.007	4750	0.372	327	0.189	11548	0.523	537	0.701	338	0.564
	HotSpot	Lvl 0	491443	/ 14484	/ 84395	/	/	/ 52344	/ 356287	/ 1073	/ 148381	/	/	/ 14559	/
		Lvl 1	/	/ 3731	0.001	3302	0.001	1256	0.002	2523	0.001	8304	0.001 222	0.001	22410	0.002	735	0.002	277	0.007
		Lvl 2	3079	0.004	4110	0.189	3723	0.001	22547	0.002	2840	0.001	19058	0.001	226	0.001	40701	0.002 2291	0.002	4131	0.001
		Lvl 3	16375	0.001	7729	0.001	6789	0.001 144914	0.002	4139	0.001	44594	0.001	330	0.005 190124	0.002 9070	0.002 10449	0.001
		NotTired	3254	0.001	3901	0.189	3110	0.001	912	0.021	1846	0.227	3844	0.001	933	0.001 11256	0.041	588	0.003	405	0.001

Table 5 :

 5 The different J9 GC policies

	Policy	Description
	Balanced	Evens out pause times & reduces the overhead of the
		costlier operations associated with GC
	Metronome	GC occurs in small interruptible steps to avoid
		stop-the-world pauses
	Nogc	Handles only memory allocation & heap expansion,
		with no memory reclaim
	Gencon (default) Minimizes GC pause times without compromising
		throughput, best for short-lived objects
	Concurrent	Minimizes the time spent in stop-the-world pauses by
	Scavenge	collecting nursery garbage in parallel with running
		application threads
	optthruput	Optimized for throughput, stopping applications for
		long pauses while GC takes place
	Optavgpause	Sacrifices performance throughput to reduce pause
		times compared to optthruput

Table 6 :

 6 The different HotSpot/GraalVM GC policies

	Policy	Description
	G1GC (default) Uses concurrent & parallel phases to achieve
		low-pauses GC and maintain good throughput
	SerialGC	Uses a single thread to perform all garbage collection
		work (no threads communication overhead)
	ParallelGC

Table 7 :

 7 Energy consumption when tuning GC settings on HotSpot, GraalVM & J9

	JVM	Mode	ALS	Avrora	Dotty	H2	Neo4j	Pmd	Reactors	Scrabble	Sunflow
		Default	2570 p-values 4153 p-values 2223 p-values 1870 p-values	5256 p-values 281 p-values 2611 p-values	410 p-values 353 p-values
		1Concurent	2567	0.403 4007	0.023 2220	1.000 1883	0.982	5368	1.000 286	0.182 2664	1.000	413	0.885 347	0.573
		1Parallel	2668	0.012 3904	0.008 2228	0.835 2022	0.000	5836	0.012 298	0.000 2869	0.144	561	0.030 317	0.000
		5Concurent	2570	0.676 4117	0.161 2215	0.210 1862	0.505	5259	1.000 282	0.980 2611	0.531	414	0.885 362	0.356
	GraalVM	5Parallel	2561	0.676 3863	0.012 2237	1.000 1910	0.103	5223	0.403 282	0.538 2682	0.531	424	0.112 353	0.758
		DisableExplicitGC	2559	0.210 3911	0.003 2215	1.000 1978	0.018	5106	0.210 281	0.758 2704	0.676	400	0.312 332	0.036
		ParallelCG	2720	0.012 4016	0.206 2237	0.531 1945	0.000 13172	0.037 282	0.878 2267	0.022	545	0.030 329	0.003
		ParallelOldGC	2715	0.012 4032	0.103 2221	1.000 1925	0.002 13362	/ 282	0.918 2514	0.012	535	0.030 329	0.008
		Default	3371 p-values 2243 p-values 3237 p-values 2107 p-values	6277 p-values 232 p-values 1644 p-values	589 p-values 510 p-values
		Balanced	9012	0.012 2232	0.597 3429	0.012 2247	0.002	8853	0.012 235	0.412 1902	0.020	661	0.061 519	0.505
		ConcurrentScavenge	3487	0.012 2270	0.280 3388	0.012 2319	0.001	6857	0.012 233	0.878 1705	0.903	639	0.194 546	0.018
		Metronome	2098	0.012 2265	0.505 3815	0.012 2717	0.000 12103	0.012 239	0.022 2089	0.020	758	0.030 422	0.000
	J9	Nogc	3454	0.022 2239	0.872 3259	0.144 2207	0.031 61781	0.012 227	0.151 1505	0.066	711	0.030 499	0.720
		Optavgpause	3601	0.012 2431	0.370 3425	0.012 2169	0.297	7495	0.012 253	0.000 1772	0.391 1089	0.030 478	0.046
		Optthruput	3357	1.000 2432	0.241 3178	0.403 2194	0.139	6324	0.835 232	0.878 1554	0.111	640	0.194 429	0.000
		ScvNoAdaptiveTenure 3494	0.012 2253	0.800 3248	0.835 2161	0.103	8442	0.012 228	0.137 1908	0.020	618	0.665 528	0.218
		Default	2765 p-values 4115 p-values 2492 p-values 1673 p-values	8152 p-values 316 p-values 1546 p-values	484 p-values 347 p-values
		1Concurent	2775	0.060 4137	0.346 2493	0.676 1675	0.918	8062	0.531 316	0.383 1533	0.665	478	0.470 334	0.218
		1Parallel	2863	0.012 4142	0.800 2526	0.037 1853	0.001	8270	0.676 334	0.000 1747	0.030	592	0.030 320	0.002
		5Concurent	2758	0.676 4091	0.872 2485	0.296 1681	0.608	8087	0.835 314	0.330 1497	0.665 469	0.030 336	0.259
		5Parallel	2767	0.144 4176	0.077 2473	0.060 1654	0.720	8046	0.835 316	0.573 1546	0.470	489	0.470 342	0.573
	HotSpot	DisableExplicitGC	2734	0.012 4062	0.448 2483	0.835 1702	0.248 7710	0.037 312	0.200 1545	0.470	470	0.061 325	0.014
		ParallelCG	2653	0.012 4064	0.629 2356	0.012 1602	0.008	8953	0.060 300	0.000 1476	0.885	579	0.030 336	0.081
		ParallelOldGC	2764	0.531 4070	0.872 2525	0.802 1675	0.959	7963	0.403 314	0.720 1582	0.194	475	0.470 333	0.151
		SerialGC	2593	0.012 4083	0.395 2378	0.012 1620	0.046 5745	0.012 307	0.002 1672	0.061	601	0.030 352	0.473

Table 8 :

 8 J-Referral recommendations.

	Project	Metric	Energy	JVM	Execution flags
	Zip4J	Least energy Most energy	2210 J 3680 J	16-sapmchn 8.0.292-J9	default default
	K-nucl	Least energy Most energy	1296 J 4433 J	21.1.r16-grl 15.0.1-J9	default -Xjit:optlevel=cold