0

I am using JDK 1.8 on Windows 10 64-bit with 8 GB of RAM. I have a Swing application. In the application I query a DB2 database. The query returns a list of database table names and query criteria for each table. These tables exist in a separate, SQL Server Express database. My Swing application creates a separate JComboBox for each SQL Server Express database table and queries that table using the received criteria and populates the JComboBox model with the query results. I loop through the results of the DB2 query and for each row I create a new JComboBox and launch a new SwingWorker to query the SQL Server Express database. Originally my app ran with JDK 1.5 on Windows 7. Of-course SwingWorker was not part of JDK in version 1.5 so I used a third party implementation. The original works fine, however after migrating to JDK 1.8 on Windows 10, it takes much more time for all of this initialization to complete. Using VisualVM, the increase in time is due to the SwingWorker threads waiting in method park() of class LockSupport. I measured the time taken to perform each individual step in the process and most of them take a few hundredths of a second to complete with the overall time for all the steps not exceeding three seconds. I tried using the SwingWorker implementation from my JDK 1.5 app in the JDK 1.8 version but the time taken did not change. How can I discover what is causing some of the SwingWorker threads to spend around 6 seconds in method park()? Alternatively, how can I change my design in order to avoid this problem?

Partial [pseudo] code...

JPanel panel = new JPanel();
Connection db2Conn = // Connect to DB2
Statement s = db2Conn.createStatement();
ResultSet rs = s.executeQuery("SQL query");
while (rs.next()) {
    new ListTask(panel, /* data from 'rs' */).execute();
}

class ListTask extends SwingWorker<Void, Void> {
    // JComboBox will be added to this. See method 'done()'
    private JPanel panel;

    // Name of table in database.
    private String tableName;

    // Criteria for querying 'tableName'.
    private List<String> criteria;

    // Results of query.
    private Object[] results;

    public ListTask(JPanel aPanel, String table, List<String> where) {
        panel = aPanel;
        tableName = table;
        criteria = where;
    }

    protected void doInBackground() {
        // Populate "results"
        return null;
    }

    protected void done() {
        JComboBox<Object> combo = new JComboBox(results);
        panel.add(combo);
    }
}

VisualVM screen capture:

VisualVM Screen Capture

Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
Abra
  • 19,142
  • 7
  • 29
  • 41

1 Answers1

0

You can see that it's blocked on LinkedBlockingQueue.take(), which means the queue is empty, so it's waiting. This is perfectly normal and correct.

Your problem is somewhere else.

Now you say you've measured the times, and the total time amounts to about 3 seconds, but then you imply that it's actually slower than that. I assume you mean it takes more than 3 seconds to get a responsive GUI. In that case it sounds like you haven't actually measured all the steps. For example if you're creating a new Connection object rather than getting one from pool, that's very slow, but it might not show up easily in VisualVM unless you know to look for it.

Finally, you could have thread contention issues that would show up with something like LockSupport.park(), but it's important to understand what it's used for. In your example you have a worker thread that has no work to do, so it's sleeping. It's a very different beast from a deadlock for example, which would show up as two parked threads (waiting for each other's locks).

So why did it work before? Maybe it worked by accident and not by design. Maybe the Java version is irrelevant, and you changed something else.

Kayaman
  • 72,141
  • 5
  • 83
  • 121
  • _Your problem is somewhere else_ Perhaps you can suggest where to look in the code in order to try and discover why the queue is empty for such a long time? (Refer to the pseudo code I posted in my question.) – Abra Oct 27 '19 at 11:12
  • @Abra there's nothing wrong with a queue being empty, especially if you have so many worker threads (the screenshot shows at least 2 pools, and at least 6 threads per pool). You're doing something wrong (I don't know what, because pseudocode is not real code), and you're misinterpreting the results of VisualVM. – Kayaman Oct 27 '19 at 12:21
  • _You're doing something wrong_ The **_same_** code works fine in Java 1.5. – Abra Oct 27 '19 at 18:47
  • @Abra okay *someone is doing something wrong*. But as I explained, there's nothing strange in the visualvm screenshot. The worker threads are waiting since there's no work to do. If you can't provide more details, it's impossible to provide more help. – Kayaman Oct 27 '19 at 19:02
  • _If you can't provide more details, it's impossible to provide more help._ Give me a hint. What details do you want me to provide? – Abra Oct 28 '19 at 08:05
  • @Abra we've determined that `LockSupport.park()` has nothing to do with your problem. I don't have your source code, I can't debug it, I can't even verify that there's a problem. I'm not playing a game here, if I knew what information is needed I would ask for it. I guess you could keep profiling and debugging the app and try to find the actual reason for the slowness. Now that you know to ignore the worker thread pool, there might still be a multi-threading / lock contention issues somewhere. – Kayaman Oct 28 '19 at 08:25
  • I look for the methods with the longest _Self time_ and those are the `LockSupport.park()` methods. If they aren't the problem, then what should I look for? If the app is running slowly, that means a method is taking a long time to execute, doesn't it? So it seems logical to me to look for methods that take a long time to execute. Should I not look for methods with long _Self time_? What things in the _VisualVM_ output should I examine? Should I use a different tool than _VisualVM_? What tools do you use to discover the cause of a slow app? – Abra Oct 28 '19 at 19:13
  • @Abra you've (partially) misunderstood [self-time](https://stackoverflow.com/questions/14626475/visualvm-and-self-time). A **worker thread waiting for work** won't slow down your program. High CPU time on the other hand indicates a performance hotspot, which might benefit from optimization. VisualVM is pretty capable, so you don't need to switch tools. You just need to interpret the results correctly (understand which threads do what, why a thread waits, etc.). I tend to use the Combined view in snapshots to identify hotspots. – Kayaman Oct 29 '19 at 06:34
  • Pardon my ignorance, but where and how in _VisualVM_ do I find **high CPU time** ? – Abra Oct 29 '19 at 11:27
  • Total time, Total time (CPU), Self time, Self time (CPU). You find it by looking for it. As "high" is subjective, it's up to you to decide whether a thread is using a lot of CPU for something or not. Slow data reading usually results in higher self-time though, because IO doesn't require a lot of CPU. It may show up as "slowness", but that depends on how your system works (i.e. are you waiting for data to display in GUI). – Kayaman Oct 29 '19 at 11:28
  • @Abra I edited the answer to include possible culprits, but as I said it's nigh impossible to remotely debug your program based on pseudocode and a partial VisualVM screenshot. – Kayaman Oct 29 '19 at 11:56