I am facing some problems with garbage collection while generating an application in java, where I use Stream.map to trim all the elements in the list. The instances of anonymous lambda class exist in the heap dump even though the instance of the enclosing class is 0 as shown in the snap of visual VM.
The LambdaTesting class:
class LambdaTesting {
protected List<String> values;
protected LambdaTesting(List<String> values) {
this.values = values;
}
public List<String> modify() {
return this.values.stream().map(x -> x.trim()).collect(Collectors.toList());
}
public List<String> modifyLocal() {
List<String> localValue = new ArrayList<>();
localValue.add("Local FOO ");
localValue.add("Local BAR ");
return localValue.stream().map(x -> x.trim()).collect(Collectors.toList());
}
}
The method which creates the instance of LambdaTesting and invokes these methods:
public List<String> testMethods() {
List<String> test = new ArrayList<>();
test.add("Global FOO ");
test.add(" GLOBAL BAR");
LambdaTesting lambdaTesting = new LambdaTesting(test);
lambdaTesting.modifyLocal();
lambdaTesting.modify();
}
The thread dump was taken after putting a debug point at the next line after testMethods is invoked.
Why are the references to Lambda still present in the heap dump?
As elaborated in Does a lambda expression create an object on the heap every time it's executed?, a non-capturing lambda expression will be remembered and reused, which implies that it is permanently associated with the code that created it. That’s not different to, e.g. string literals whose object representation stays in memory as long as the code containing the literal is alive.
This is an implementation detail. It doesn’t have to be that way, but the reference implementation and hence, all commonly used JREs do it that way.
A non-capturing lambda expression is a lambda expression that uses no (non-constant) variables of the surrounding context and does not use this, neither implicitly nor explicitly. So it bears no state, hence, consumes a tiny amount of memory. There is also no possibility to create a leak regarding other objects, as having references to other objects is what makes the difference between non-capturing and capturing lambda expressions and likely is the main reason why capturing lambda expressions are not remembered that way.
So the maximum number of such never-collected instances is equal to the total number of lambda expression in your application, which might be a few hundred or even thousands, but still small compared to the total number of objects the application will ever create. As explained in Function.identity() or t->t, putting a lambda expression into a factory method instead of repeating it in the source code, can reduce the number of instances. But given the rather small total number of objects, that’s rarely a concern. Compare with the number of the already mentioned string literals or the Class objects which already exist in the runtime…
Related
This question already has answers here:
Eligibility for escape analysis / stack allocation with Java 7
(3 answers)
Closed 5 years ago.
Since somewhere around Java 6, the Hotspot JVM can do escape analysis and allocate non-escaping objects on the stack instead of on the garbage collected heap. This results in a speedup of the generated code and reduces pressure on the garbage collector.
What are the rules for when Hotspot is able to stack allocate objects? In other words when can I rely on it to do stack allocation?
edit: This question is a duplicate, but (IMO) the answer below is a better answer than what is available at the original question.
I have done some experimentation in order to see when Hotspot is able to stack allocate. It turns out that its stack allocation is quite a bit more limited than what you might expect based on the available documentation. The referenced paper by Choi "Escape Analysis for Java" suggests that an object that is only ever assigned to local variables can always be stack allocated. But that is not true.
All of this are implementation details of the current Hotspot implementation, so they could change in future versions. This refers to my OpenJDK install which is version 1.8.0_121 for X86-64.
The short summary, based on quite a bit of experimentation, seems to be:
Hotspot can stack-allocate an object instance if
all its uses are inlined
it is never assigned to any static or object fields, only to local variables
at each point in the program, which local variables contain references to the object must be JIT-time determinable, and not depend on any unpredictable conditional control flow.
If the object is an array, its size must be known at JIT time and indexing into it must use JIT-time constants.
To know when these conditions hold you need to know quite a bit about how Hotspot works. Relying on Hotspot to definately do stack allocation in a certain situation can be risky, as a lot of non-local factors are involved. Especially knowing if everything is inlined can be difficult to predict.
Practically speaking, simple iterators will usually be stack allocatable if you just use them to iterate. For composite objects only the outer object can ever be stack allocated, so lists and other collections always cause heap allocation.
If you have a HashMap<Integer,Something> and you use it in myHashMap.get(42), the 42 may stack allocate in a test program, but it will not in a full application because you can be sure that there will be more than two types of key objects in HashMaps in the entire program, and therefore the hashCode and equals methods on the key won't inline.
Beyond that I don't see any generally applicable rules, and it will depend on the specifics of the code.
Hotspot internals
The first important thing to know is that escape analysis is performed after inlining. This means that Hotspot's escape analysis is in this respect more powerful than the description in the Choi paper, since an object returned from a method but local to the caller method can still be stack allocated. Because of this iterators can nearly always be stack allocated if you do e.g. for(Foo item : myList) {...} (and the implementation of myList.iterator() is simple enough, which they usually are.)
Hotspot only compiles optimized versions of methods once it determines the method is 'hot', so code that is not run a lot of times does not get optimized at all, in which case there is no stack allocation or inlining whatsoever. But for those methods you usually don't care.
Inlining
Inlining decisions are based on profiling data that Hotspot collects first. The declared types do not matter so much, even if a method is virtual Hotspot can inline it based on the types of the objects it sees during profiling. Something similar holds for branches (i.e. if-statements and other control flow constructs): If during profiling Hotspot never sees a certain branch being taken, it will compile and optimize the code based on the assumption that the branch is never taken. In both cases, if Hotspot cannot prove that its assumptions will always be true, it will insert checks in the compiled code known as 'uncommon traps', and if such a trap is hit Hotspot will de-optimize and possibly re-optimize taking the new information into account.
Hotspot will profile which object types occur as receivers at which call sites. If Hotspot only sees a single type or only two distinct types occuring at a call site, it is able to inline the called method. If there are only one or two very common types and other types occur much less often Hotspot should also still be able to inline the methods of the common types, including a check for which code it needs to take. (I'm not entirely sure about this last case with one or two common types and more uncommon types though). If there are more than two common types, Hotspot will not inline the call at all but instead generate machine code for an indirect call.
'Type' here refers to the exact type of an object. Implemented interfaces or shared superclasses are not taken into account. Even if different receiver types occur at a call site but they all inherit the same implementation of a method (e.g. multiple classes that all inherit hashCode from Object), Hotspot will still generate an indirect call and not inline. (So i.m.o. hotspot is quite stupid in such cases. I hope future versions improve this.)
Hotspot will also only inline methods that are not too big. 'Not too big' is determined by the -XX:MaxInlineSize=n and -XX:FreqInlineSize=n options. Inlinable methods with a JVM bytecode size below MaxInlineSize are always inlined, methods with a JVM bytecode size below FreqInlineSize are inlined if the call is 'hot'. Larger methods are never inlined. By default MaxInlineSize is 35 and FreqInlineSize is platform dependent but for me it is 325. So make sure your methods are not too big if you want them inlined. It can sometimes help to split out the common path from a large method, so that it can be inlined into its callers.
Profiling
One important thing to know about profiling is that profiling sites are based on the JVM bytecode, which itself is not inlined in any way. So if you have e.g. a static method
static <T,U> List<U> map(List<T> list, Function<T,U> func) {
List<U> result = new ArrayList();
for(T item : list) { result.add(func.call(item)); }
return result;
}
that maps a SAM Function callable over a list and returns the transformed list, Hotspot will treat the call to func.call as a single program-wide call site. You might call this map function at several spots in your program, passing a different func in at each call site (but the same one for one call site). In that case you might expect that Hotspot is able to inline map, and then also the call to func.call since at every use of map there is only a single func type. If this were so, Hotspot would be able to optimize the loop down very tightly. Unfortunately Hotspot is not smart enough for that. It only keeps a single profile for the func.call call site, lumping all the func types that you pass to map together. You will probably use more than two different implementations of func, so Hotspot will not be able to inline the call to func.call. Link for more details, and archived link as the original appears to be gone.
(As an aside, in Kotlin the equivalent loop can be fully inlined as the Kotlin compiler can do inlining of calls at the bytecode level. So for some uses it could be significantly faster than Java.)
Scalar Replacement
Another important thing to know is that Hotspot does not actually implement stack allocation of objects. Instead it implements scalar replacement, which means that an object is deconstructed into its constituent fields and those fields are stack allocated like normal local variables. This means that there is no object left at all. Scalar replacement only works if there is never a need to create a pointer to the stack-allocated object. Some forms of stack allocation in e.g. C++ or Go would be able to allocate full objects on the stack and then pass references or pointers to them to called functions, but in Hotspot this does not work. Therefore if there is ever a need to pass an object reference to a non-inlined method, even if the reference would not escape the called method, Hotspot will always heap-allocate such an object.
In principle Hotspot could be smarter about this, but right now it is not.
Test program
I used the following program and variations to see when Hotspot will do scalar replacement.
// Minimal example for which the JVM does not scalarize the allocation. If field is final, or the second allocation is unconditional, it will.
class Scalarization {
int field = 0xbd;
long foo(long i) { return i * field; }
public static void main(String[] args) {
long result = 0;
for(long i=0; i<100; i++) {
result += test();
}
System.out.println("Result: "+result);
}
static long test() {
long ctr = 0x5;
for(long i=0; i<0x10000; i++) {
Scalarization s = new Scalarization();
ctr = s.foo(ctr);
if(i == 0) s = new Scalarization();
ctr = s.foo(ctr);
}
return ctr;
}
}
If you compile and run this program with javac Scalarization.java; java -verbose:gc Scalarization you can see if scalar replacement worked by the number of garbage collections. If scalar replacement works, no garbage collection happened on my system, if scalar replacement did not work I see a few garbage collections.
Variants that Hotspot is able to scalarize run significantly faster than versions where it does not. I verified the generated machine code (instructions) to make sure Hotspot was not doing any unexpected optimizations. If hotspot is able to scalar replace the allocations, it can then also do some additional optimizations on the loop, unrolling it a few iterations and then combining those iterations together. So in the scalarized versions the effective loop count is lower with each iteraton doing the work of multiple source code level iterations. So the speed difference is not only due to allocation and garbage collection overhead.
Observations
I tried a number of variations on the above program. One condition for scalar replacement is that the object must never be assigned to an object (or static) field, and presumably also not into an array. So in code like
Foo f = new Foo();
bar.field = f;
the Foo object cannot be scalar replaced. This holds even if bar itself is scalar replaced, and also if you never again use bar.field. So an object can only ever be assigned to local variables.
That alone is not enough, Hotspot must also be able to determine statically at JIT-time which object instance will be the target of a call. For example, using the following implementations of foo and test and removing field causes heap allocation:
long foo(long i) { return i * 0xbb; }
static long test() {
long ctr = 0x5;
for(long i=0; i<0x10000; i++) {
Scalarization s = new Scalarization();
ctr = s.foo(ctr);
if(i == 50) s = new Scalarization();
ctr = s.foo(ctr);
}
return ctr;
}
While if you then remove the conditional for the second assignment no more heap allocation occurs:
static long test() {
long ctr = 0x5;
for(long i=0; i<0x10000; i++) {
Scalarization s = new Scalarization();
ctr = s.foo(ctr);
s = new Scalarization();
ctr = s.foo(ctr);
}
return ctr;
}
In this case Hotspot can determine statically which instance is the target for each call to s.foo.
On the other hand, even if the second assignment to s is a subclass of Scalarization with a completely different implementation, as long as the assignment is unconditional Hotspot will still scalarize the allocations.
Hotspot does not appear to be able to move an object to the heap that was previously scalar replaced (at least not without deoptimizing). Scalar replacement is an all-or-nothing affair. So in the original test method both allocations of Scalarization always happen on the heap.
Conditionals
One important detail is that Hotspot will predict conditionals based on its profiling data. If a conditional assignment is never executed, Hotspot will compile code under that assumption, and then might be able to do scalar replacement. If at a later point in time the condtion does get taken, Hotspot will need to recompile the code with this new assumption. The new code will not do scalar replacement since Hotspot can no longer determine the receiver instance of following calls statically.
For instance in this variant of test:
static long limit = 0;
static long test() {
long ctr = 0x5;
long i = limit;
limit += 0x10000;
for(; i<limit; i++) { // In this form if scalarization happens is nondeterministic: if the condition is hit before profiling starts scalarization happens, else not.
Scalarization s = new Scalarization();
ctr = s.foo(ctr);
if(i == 0xf9a0) s = new Scalarization();
ctr = s.foo(ctr);
}
return ctr;
}
the conditional assignemnt is only executed once during the lifetime of the program. If this assignment occurs early enough, before Hotspot starts full profiling of the test method, Hotspot never notices the conditional being taken and compiles code that does scalar replacement. If profiling has already started when the conditional is taken, Hotspot will not do scalar replacement. With the test value of 0xf9a0, whether scalar replacement happens is nondeterministic on my computer, since exactly when profiling starts can vary (e.g. because profiling and optimized code is compiled on background threads). So if I run the above variant it sometimes does a few garbage collections, and sometimes does not.
Hotspot's static code analysis is much more limited than what C/C++ and other static compilers can do, so Hotspot is not as smart in following the control flow in a method through several conditionals and other control structures to determine the instance that a variable refers to, even if it would be statically determinable for the programmer or a smarter compiler. In many cases the profiling information will make up for that, but it is something to be aware of.
Arrays
Arrays can be stack allocated if their size is known at JIT time. However indexing into an array is not supported unless Hotspot can also statically determine the index value at JIT-time. So stack allocated arrays are pretty useless. Since most programs don't use arrays directly but use the standard collections this is not very relevant, as embedded objects such as the array containing the data within an ArrayList already need to be heap-allocated due to their embedded-ness. I suppose the reasoning for this restriction is that there exists no indexing operation on local variables so this would require additional code generation functionality for a pretty rare use case.
Let's say I am trying to sort a collection with a specific Comparator. Does it matter from a performance point of view to have a comparator defined in the sorted() clause of as a an anonumous instance, or it is better to create an instance once and just call compare method in the sorted() clause?
In essence, what is better:
myCollection.stream().sorted(
new Comparator<String>(){
public int compare(String a, String b){
//code
}
})
Comparator<String> comp = new MyCustomComparator<>();
myCollection.stream().sorted(comp::compare)
Note: neither syntax, nor comparing values matter - I want to know conceptually whether JVM is smart enough to initialize my anonymous comparator only once (case 1) and keep reusing just one method, or it will keep creating new instances (then I would choose case 2)
A new instance of an anonymous class will be created every time the expression using new is evaluated.
In your first example, a new one is created every time the statement runs where you are passing it to sorted.
In your second example, a new one is created wherever the comp variable is being initialized. If comp is an instance member, then it gets created whenever the object that owns it is created. If comp is a local variable in a method, then it gets created every time the method is called.
A static, stateless and non-capturing Comparator is always going to be the most efficient way, because you can create it once and keep it forever. (See for example String.CASE_INSENSITIVE_ORDER.)
That's not to say you shouldn't use another way.
In Java 8, you should prefer lambdas over anonymous classes. Non-capturing lambdas can be cached and only created once. For example, this program outputs true:
class Example {
public static void main(String[] args) {
System.out.println(comparator() == comparator());
}
static Comparator<String> comparator() {
return (lhs, rhs) -> lhs.compareTo(rhs);
}
}
(Example on Ideone.)
All that said, you shouldn't worry about creating a few small objects here in there in Java, because it's unavoidable and the garbage collector is optimized for it. The vast majority of the time, the "best" way to do something is also the most readable.
Note that you do not have to use a method reference in your second example. You can pass it to the method directly:
Comparator<String> comp = new MyCustomComparator<>();
myCollection.stream().sorted(comp)...
Runtime for both approach will be the same. This can be expressed as below :
For 1st scenario, first JVM create instance of comparator with your custom code for compare method and allocated space for this object anonymously. So ultimately object is created and allocated memory without some pointing reference for user and once function call is over object is registered for GC.
For 2nd scenario, JVM again created new instance of comparator with custom code and allocated space and also provide reference stored in separate variable so that this object can be used again but here object won't be collected by GC if same is used again in code anywhere else. So when GC runs for next time, it has to scan for references of variable and figure out whether it can be GCed or not.
Consider this method:
private void iterate(List<Worker> workers) {
SortedSet<Worker> set = new TreeSet<>(new Comparator<Worker>() {
#Override
public int compare(Worker w0, Worker w1) {
return Double.compare(w0.average, w1.average);
}
});
// ...
}
As you can see the set is creating a new TreeSet with a custom comparator.
I was wondering if it makes any difference from a performance/memory/garbage collection/whatever point of view, if I were to do this and instead having polluted the outer space:
static final Comparator<Worker> COMPARATOR = new Comparator<Worker>() {
#Override
public int compare(Worker w0, Worker w1) {
return Double.compare(w0.average, w1.average);
}
};
private void iterate(List<Worker> workers) {
SortedSet<Worker> set = new TreeSet<>(COMPARATOR);
// ...
}
The reason I am asking, is that I feel the compiler should already figure this out and optimize it for me, so I shouldn't have to extract it, right?
The same thing goes for Strings or other temporary, immutable objects declared within a method.
Would it make any difference to extract it a final variable?
Note: I am aware of the little impact of performance boost this might give. The question is whether there is any difference, howsoever negligible.
There will be a difference yes.
CPU affect: assigning to a static reduces the amount of work necessary to allocate a new comparator every time
GC effect: assigning a new object every time and then immediately discarding it will have no young GC cost; however assigning it to a variable will increase GC times (very very marginally) as it is an extra set of references that will need to be walked. Dead objects cost nothing, live objects do.
Memory effect: assigning the comparator to a constant will reduce how much memory each invocation of the method will require, in exchange for a low constant overhead that will be moved into tenured GC space.
Risk of reference escapes: Inner classes contain pointers to the class that constructed it. If the inner class (Comparator) was ever returned out of the method that created it, then a strong reference to the parent object could escape and prevent GC of the parent. Purely a gotcha that can creep into code, it is not a problem in this example.
Hotspot is very good at inlining but it is unlikely to recognise that the comparator can be allocated on heap or moved to a constant. But that will depend on the contents of TreeSet. If the implementation of TreeSet was very simple (and small) then it could get inlined, however we all know that it is not. Also TreeSet is coded to be generic, if it was only ever used with one type of object (Worker) then there are some optimisations that the JVM can apply however we should assume that TreeSet will get used by other types too and so TreeSet will not be able to make any assumptions about the Comparator that is being past into it.
Thus the difference between the two versions is primarily an object allocation. The use of the final keyword is unlikely to improve performance as Hotspot mostly ignores the final keyword anyway.
Java 8 has a very interesting behaviour here when using lambdas. Consider the following variant of your example:
import java.util.*;
public class T {
public void iterate(List<String> workers) {
SortedSet<Double> set = new TreeSet<>( Double::compare );
}
}
Run 'javap -c T.class', and you will see the following jvm code:
public void iterate(java.util.List<java.lang.String>);
Code:
0: new #2 // class java/util/TreeSet
3: dup
4: invokedynamic #3, 0 // InvokeDynamic #0:compare:()Ljava/util/Comparator;
9: invokespecial #4 // Method java/util/TreeSet."<init>":(Ljava/util/Comparator;)V
12: astore_2
13: return
The cool thing to note here is that there is no object construction for the lambda. invokedynamic will have a higher cost the first time that it is called and then it gets effectively cached.
One big difference is caused by the "hidden" implication that anonymous classes hold an implicit reference to the containing class, so if you pass that TreeSet to another process, a reference to your class instance is held via the TreeSet via the anonymous Comparator by another piece of code, so your instance ca't be garbage collected.
That can cause a memory leak.
Option 2 however doesn't suffer from that problem.
Otherwise, it's a matter of style.
In java 8, you can use a lambda expression instead, which is the best of both worlds.
The purpose of this query is to compare one aspect of Java and C++, that has to do with the "new" operator.
Now, I know that in C++ there are two ways to create objects; with or without the "new" operator. In the absence of that operator, space is not allocated in the heap region, whereas, in its presence, space is allocated in the heap region.
What about Java? I notice that the "new" operator is used for creating every object. Even arrays are created with the "new" operator. Does it mean that in Java there is only one place for objects to exist in - that is, the heap region?
Thanks.
Yes, the new operator always allocates memory for the object on the heap. Unlike C++, objects in Java cannot be created on the stack.
Local primitive types, and local references to object types, both take up "stack" memory, as do they both when passed as parameters to methods.
All objects themselves exist in the equivalent of a "heap".
All Java objects (i.e. all things with a reference) are allocated in the heap1 from the perspective of the application and the application programmer2. Java does not support explicit allocation of objects on the stack. Object references can be stored both in heap nodes (i.e. class or instance fields) and stack frames (i.e. local variables, etc).
In fact, there are a few ways that first class Java objects can be created in Java that don't involve using the new keyword.
The { ... } array initializer syntax can be used in an array declaration without the new keyword.
A String literal involves the creation of a String object (at class load time).
The boxing conversion will (typically) create a new wrapper object without an explicit new or method call.
The reflective newInstance and similar methods create objects without an explicit new.
Under the hood, the implementation of Java serialization uses a special method in the Unsafe class to create objects without executing any declared constructor.
You can also create Java objects in native code using the JNI / JNA apis.
(There is a strong argument that the last two are "not Java", but they are worth mentioning anyway. And the String literal and auto-boxing cases involve Java code that uses new under the hood.)
1 - There can be more than one heap, though this is transparent to the application.
2 - The latest Hotspot JVMs have an experimental "escape analysis" feature that determines whether objects "escape" from the context in which they are created. Objects that don't escape could be safely allocated on the stack. Once again, this optimization is transparent to the application.
In Java, all objects are dynamically allocated on Heap. This is different from C++ where objects can be allocated memory either on Stack or on Heap. In C++, when we allocate abject using new(), the abject is allocated on Heap, otherwise on Stack if not global or static.
In Java, when we only declare a variable of a class type, only a reference is created (memory is not allocated for the object). To allocate memory to an object, we must use new(). So the object is always allocated memory on heap.
Example 1: Give Compile Error.
class Test {
void show() {
System.out.println("Test::show() called");
}
}
public class Main {
public static void main(String[] args) {
Test t;
t.show(); // Error here because t is not initialed
}
Example 2: Allocating memory using new() makes above program work.
class Test {
void show() {
System.out.println("Test::show() called");
}
}
public class Main {
public static void main(String[] args) {
Test t = new Test(); //all objects are dynamically allocated
t.show(); // No error
}
}
I need to call methods of a class with multiple methods very often in a simulation loop.
Some of these methods need to access temporary objects for storing information in them. After leaving the method the stored data is not needed anymore.
For example:
Class class {
method1() {
...
SomeObject temp = new SomeObject();
...
}
method2() {
...
SomeObject temp = new SomeObject();
SomeObject temp2 = new SomeObject();
...
}
}
I need to optimize as much as possible. The most expensive (removable) problem is that too many allocations happen.
I assume it would be better not to allocate the space needed for those objects every time so I want to keep them.
Would it be more efficient to store them in a static way or not?
Like:
Class class {
private (static?) SomeObject temp;
private (static?) SomeObject temp2;
methods...
}
Or is there even a better way? Thank you for your help!
Edit based on answers:
Not the memory footprint is the actual problem but the garbage collection cleaning up the mess.
SomeObject is a Point2D-like class, nothing memory expensive (in my opinion).
I am not sure whether it is better to use (eventually static) class level objects as placeholder or some more advanced method which I am not aware of.
I would be wary in this example of pre-mature optimization. There are downsides, typically, that it makes the code more complex (and complexity makes bugs more likely), harder to read, could introduce bugs, may not offer the speedup you expected, etc. For a simple object such as representing a 2D point coordinate, I wouldn't worry about re-use. Typically re-use gains the most benefit if you are either working with a large amount of memory, avoid lengthy expensive constructors, or are pulling object construction out of a tight loop that is frequently executed.
Some different strategies you could try:
Push responsiblity to caller One way would be to to have the caller pass in an object pre-initialized, making the method parameter final. However, whether this will work depends on what you need to do with the object.
Pointer to temporary object as method parameter Another way would be to have the caller pass as an object as a parameter that's purpose is essentially to be a pointer to an object where the method should do its temporary storage. I think this technique is more commonly used in C++, but works similarly, though sometimes shows up in places like graphics programming.
Object Pool One common way to reuse temporary objects is to use an object pool where objects are allocated from a fixed bank of "available" objects. This has some overhead, but if the objects are large, and frequently used for only short periods of time, such that memory fragmentation might be a concern, the overhead may be enough less to be worth considering.
Member Variable If you are not concerned about concurrent calls to the method (or have used synchronization to prevent such), you could emulate the C++ism of a "local static" variable, by creating a member variable of the class for your storage. It makes the code less readable and slightly more room to introduce accidental interference with other parts of your code using the variable, but lower overhead than an object pool, and does not require changes to your method signature. If you do this, you may optionally also wish to use the transient keyword on the variable as well to indicate the variable does not need to be serialized.
I would shy away from a static variable for the temporary unless the method is also static, because this may have a memory overhead for the entire time your program runs that is undesirable, and the same downsides as a member variable for this purpose x2 (multiple instances of the same class)
Keep in mind that temp and temp2 are not themselves objects, but variables pointing to an object of type SomeObject. The way you are planning to do it, the only difference would be that temp and temp2 would be instance variables instead of local variables. Calling
temp = new SomeObject();
Would still allocate a new SomeObject onto the heap.
Additionally, making them static or instance variables instead of local would cause the last assigned SomeObjects to be kept strongly reachable (as long as your class instance is in scope for instance variables), preventing them from being garbage collected until the variables are reassigned.
Optimizing in this way probably isn't effective. Currently, once temp and temp2 are out of scope, the SomeObjects they point to will be eligible for garbage collection.
If you're still interested in memory optimization, you will need to show what the SomeObject is in order to get advice as to how you could cache the information it's holding.
How large are these objects. It seems to me that you could have class level objects (not necessarily static. I'll come back to that). For SomeObject, you could have a method that purges its contents. When you are done using it in one place, call the method to purge its contents.
As far as static, will multiple callers use this class and have different values? If so, don't use static.
First, you need to make sure that you are really have this problem. The benefit of a Garbage Collector is that it takes care of all temporary objects automatically.
Anyways, suppose you run a single threaded application and you use at most MAX_OBJECTS at any giving time. One solution could be like this:
public class ObjectPool {
private final int MAX_OBJECTS = 5;
private final Object [] pool = new Object [MAX_OBJECTS];
private int position = 0;
public Object getObject() {
// advance to the next object
position = (position + 1) % MAX_OBJECTS;
// check and create new object if needed
if(pool[position] == null) {
pool[position] = new Object();
}
// return next object
return pool[position];
}
// make it a singleton
private ObjectPool() {}
private static final ObjectPool instance = new ObjectPool();
public static ObjectPool getInstance() { return instance;}
}
And here is the usage example:
public class ObjectPoolTest {
public static void main(String[] args) {
for(int n = 0; n < 6; n++) {
Object o = ObjectPool.getInstance().getObject();
System.out.println(o.hashCode());
}
}
}
Here is the output:
0) 1660364311
1) 1340465859
2) 2106235183
3) 374283533
4) 603737068
5) 1660364311
You can notice that the first and the last numbers are the same - the MAX_OBJECTS + 1 iterations returns the same temporary object.