-2

I'm setting a class via SOOT-ECLIPSE plugin as the main class and want it to operate like a singleton. But my implementation seems to not work, as I get different instances one every run.

I tried to use a wrapper and call the singleton class from there in order to avoid the case in which this class is garbage collected by the classloader of soot. But I get different instances, as well.

I confirmed that it runs on one JVM, as the PID that I get on every run is the same in contrast to the instance of the class that changes on every run.

I would really appreciate any insight into this one.

public class MyMain{

    private static boolean isFirstInstance = true;

    private static class MyMainHolder {
        private static final MyMain INSTANCE = new MyMain();
    }

    public static synchronized MyMain getInstance() {
        return MyMainHolder.INSTANCE;
    }

    private MyMain() {
        if (MyMainHolder.INSTANCE != null) {
            throw new IllegalStateException("Already instantiated");
        }
    }


    public static void main(String[] args) {
    System.out.println("PID: " + ManagementFactory.getRuntimeMXBean().getName());

    MyMain tmp = getInstance();
    }
eternalStudent
  • 444
  • 2
  • 19
  • Circular dependency? You're reading `MyMainHolder.INSTANCE` AS you're writing to it with a `new MyMain()`, that's definitely not going to work... – Unihedron Jul 04 '14 at 14:53
  • 1
    How are you running it? You make it sound like you're launching it separate times. Even if you have a JVM that for some reason launches multiple applications in the same JVM process, they might still be completely separate applications due to different classloaders, etc. – Mark Peters Jul 04 '14 at 14:54
  • what is the purpose of `MyMainHolder` class? Why don't store `private static final MyMain INSTANCE` directly in `MyMain` ? –  Jul 04 '14 at 14:56
  • @Unihedron: Not sure what you're getting at. Seems to me like it would work fine, except that the `MyMainHolder.INSTANCE != null` check, while fine, is probably overkill (and vulnerable to a race condition) if you're making the constructor private anyway. – Mark Peters Jul 04 '14 at 14:56
  • 1
    "But it seems to not work as well" - in what way? It's entirely unclear what's wrong here. Are you expecting one *persistent* instance, across many VMs? – Jon Skeet Jul 04 '14 at 14:58
  • 1
    @RafaelOsipov: It's a textbook implementation of the mostly obsolete [Initialization on Demand Holder idiom](http://en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom). These days an enum is typically encouraged instead. – Mark Peters Jul 04 '14 at 14:58
  • 1
    @RafaelOsipov As Mark Peters mentrioned, it's the initialization on demand holder, I chose this one after te simple private static INSTANCE failed – eternalStudent Jul 04 '14 at 15:02
  • @MarkPeters I run it from 'SOOT' platform. It uses this class as the main class. I also had the same impression as you that's why I tried also with the 'Wrapper' class but still I get different instances of MyMain – eternalStudent Jul 04 '14 at 15:05
  • @JonSkeet It seems not to work either, I still get different instances. I have one JVM and I want one instance of this class – eternalStudent Jul 04 '14 at 15:06
  • @emma: "It seems not to work" still doesn't explain what you mean. How are you seeing multiple instances *in the same JVM* (i.e. the same process, running once, with one classloader)? If you call `getInstance()` twice, you should receive the same instance twice. If you're actually running the whole program twice (i.e. separate processes, whether at the same time or one after another) then that's an entirely different matter. – Jon Skeet Jul 04 '14 at 15:09
  • @emma: This pattern guarantees you will only get one instance of this class. However, the nature of classloading is that this class file could, depending on how you're loading it, be loaded *more than once* in the same JVM. You could get the same class loaded twice by two disjoint classloaders, each guaranteed to have a single `INSTANCE`. This is for example how application containers like Tomcat sandbox applications from each other, even though they run in the same JVM. What is this SOOT platform? Google's coming up empty, other than some bytecode enhancer framework. – Mark Peters Jul 04 '14 at 15:09
  • @JonSkeet My point is that I want to run this class and get the same instance every time. When I run it now, i see the same 'PID', but with instance.hashCode(); I see different instances. That's my query, how to make it possible. – eternalStudent Jul 04 '14 at 15:11
  • @emma: If you're invoking Java more than once *of course* they'll be different instances! Java objects don't outlive the lifetime of the JVM. You need to give more info on how you're running this. – Mark Peters Jul 04 '14 at 15:13
  • @MarkPeters This is soot: http://www.sable.mcgill.ca/soot/ Mark, I understand this matter but I am not sure how to solve it. I tried with wrapping the main class but still I deal with the same problem. I use soot-eclipse plugin, and give my class as the main class in order to run soot from there. My problem is that I want it to be a singleton. – eternalStudent Jul 04 '14 at 15:14
  • Why is it important to you that the hashcode is the same in each invocation? – Jamie Cockburn Jul 04 '14 at 15:31

2 Answers2

5

(From comments...)

My point is that I want to run this class and get the same instance every time.

It sounds like you have a misconception about what singletons are. If you're running your app twice, i.e.

$ java MyApp
[output]

$ java MyApp
[output]

then those are entirely separate invocations of the JVM. When the first run has finished, there is no instance any more - all the memory allocated by the JVM will have been released.

If you need a persistent object (i.e. one which still exists somewhere even when Java isn't running) then that's a very different scenario, and you should look into serialization - although you still shouldn't expect two applications running on separate instances of the JVM to see the same object in memory.

EDIT: Now that we know you're running under Soot, it's possible that that's creating a separate classloader every time, so you'd get a different instance that way instead. Fundamentally, you need to understand the context in which your code is running, and provide that information (and exactly what you're observing) in the question.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Nop, I dont do that. I don't run it twice. As I said in one comment, I just give it as the main class on an eclipse-plugin (soot specifically) – eternalStudent Jul 04 '14 at 15:17
  • 1
    @emma: Basically it sounds like there's a bunch more information that *really* should be in the question. The information that this is running in Eclipse shouldn't be buried in the 14th comment! And we still don't know how you're observing the two instances. – Jon Skeet Jul 04 '14 at 15:19
  • 1
    @emma: Really this is up to the implementation details of `soot`. If it's an Eclipse plugin that can run Java code, it's running in Eclipse's JVM, but it is probably sandboxing using a separate classloader. You should probably think of separate runs as being in separate JVMs. – Mark Peters Jul 04 '14 at 15:19
  • @MarkPeters Do you think that it would be possible to do that if this class was a singleton inside soot? (and not as an external class , as it is now) – eternalStudent Jul 04 '14 at 15:28
1

It appears that to OP actually want to be able to discrimiate between the first run of the program, and subsequent ones. This will do that:

public static void main(String[] args) {
    File file = new File("has_been_run");
    if (!file.exists()) {
        try {
            file.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
        }
        // first time
    } else {
        // subsequent times
    }
}

Obviously, is the file is deleted somehow, then that counts as a "first run" again.

So could do something similar with any persistent data store that you have available.

Jamie Cockburn
  • 7,379
  • 1
  • 24
  • 37
  • And for `cmovl` compiler optimization, `return instance == null ? instance = new MyMain() : instance;`. – Unihedron Jul 04 '14 at 15:12
  • 2
    I wouldn't use this approach, personally - I'd generally just go for either an enum with a single value (for various reasons) or simply `private static final MyMain instance = new MyMain();`. For *full* laziness, I'd use the resource holder approach as shown in the OP's code. But your "answer" really isn't answering the question at all. – Jon Skeet Jul 04 '14 at 15:15
  • @JamieCockburn I also tried this one, but I deal with the same problem. My problem is how to get it to work as a singleton when it runs from another application. – eternalStudent Jul 04 '14 at 15:19
  • In that case, that is not a singleton in the traditional sense. You cannot have the same *instance* of a class across multiple invocations of your code (in different JVMs for example). What are you actually trying to achieve? Are you trying to have the instance data from one invocation, be carried over to the next? – Jamie Cockburn Jul 04 '14 at 15:24
  • @JamieCockburn What I want to do is to descriminate the first run of the program from the rest, I thought that by having a singleton I could manipulate if it is the first run (as instance is null) – eternalStudent Jul 04 '14 at 15:31
  • No, it doesn't work like that. You need to use a persistent data store outside of java for that. You could check for the existence of a file on disk, and then create it is it does not exist. If the file is not there, it's the first run, if it it, then it's not the first run. – Jamie Cockburn Jul 04 '14 at 15:32
  • See my edit to my answer for a solution. – Jamie Cockburn Jul 04 '14 at 15:36