8

I recently noticed a specific part of my code is running significantly slower since I updated to Java 7. Pretty surprising as Java 7 is globally faster than Java 6.

The program is pretty large but I succeeded to extract a reproducible code to demonstrate how java 7 is slower than Java 6.

If you run the exact same code with Java 6 and Java 7 you see Java 7 is approximately 7-8 times slower than Java 6 !!!

Here is the code (don't bother trying to understand what it does, it's just an abstract from the real code and as such does not really make sense). It's basically a code that parse a string to format some part of it:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Window;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.text.SimpleDateFormat;
import java.util.Calendar;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultStyledDocument;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
public class CPerfJava7 extends JFrame {
    private static final long serialVersionUID = 1L;

    // +==============================================================+
    // | Attributes                                                   |
    // +==============================================================+
    JPanel mainPanel;

    public CPseudoHtmlDocument docu;
    private static long totalTime = 0;

    // +==============================================================+
    // | Constructors                                                 |
    // +==============================================================+

    public CPerfJava7() {
        super("PerfJava7");
        docu = new CPseudoHtmlDocument();
        mainPanel = new JPanel(new BorderLayout());
        JTextPane textPane = new JTextPane();
        textPane.setDocument(docu);
        textPane.setText("testcase one\ndo program breakfast meal\ndo program time\ndo program portion size - 1/4 of the cup\nIt was easy to set up the time and portion size as directed in the manual\n\ndo collect kibbles from the bowl and measure the breakfast portion\ncheck the portion dispensed 1/4 cup of kibbles \nActual result - it was less than 1/4 cup. The wheel that rotates and dispenses\n the kibbles might haven't rotated completely.\n\ntestcase two\ndo program lunch meal\ndo program time\ndo leave the portion size the same - 1/4 of the cup\n\ncheck the portion dispensed 1/4 cup of kibbles\n do collect kibbles from the bowl and measure the lunch portion\nActual result was 1/20 of the cup, the wheel dispenser hardly released any kibbles\n\ntestcase three\n\ndo program dinner meal\ndo program time\ndo leave the portion size the same - 1/4 of the cup\n\ncheck the portion dispensed 1/4 cup of kibbles\n do collect kibbles from the bowl and measure the dinner portion\nActual result was  less than 1/4 cup\n\nWhy was the middle meal the smallest? Is lunch always the smallest?\nTo prove this hypothesis, repeat first three test cases again.\n\ntestcase four\ndo repeat test case one\nActual result - it was less 1/4 and the portion was dispensed in two increments.\n\ntestcase five\ndo repeat test case two\nActual result - 1/8 of the cup.\n\ntestcase six\ndo repeat test case three\nActual result - 1/2 cup! The wheel rotated almost twice. It looked like all\n kibbles which were not dispensed previously, got released.\n Lunch was still the smallest meal, but the dinner portion was twice of the size\n.All three tests must be repeated again.\n\ntestcase seven\ndo repeat test case one\nActual result - 1/4 of the cup. Great!\n\ntestcase eight\ndo repeat test case two\nActual result - 1/4 of the cup! It works perfectly! Why?\n\ntestcase nine\ndo repeat test case three\nActual result - 1/6, so the third meal was less than it should be. Is this size of the portion hard to dispense correctly? Let's try 1/2 of the cup\n\ntestcase ten\ndo program breakfast meal\ndo program time\ndo program 1/2 of the cup\ncheck the amount of kibbles dispensed is 1/2 of the cup\nActual result - 1/2 of the cup. Excellent. The wheel rotated twice as designed. In a minute it added 1/4 of the cup.\n\ntestcase eleven\ndo program lunch meal\ndo program time\ndo leave the portion size the same\ncheck the amount of kibbles dispensed is 1/2 of the cup\nActual result - 1/3 of the cup. Lunch is smaller again.\n\testcase twelve\ndo program dinner meal\ndo program time\ndo leave the portion size the same - 1/2 of the cup\ncheck the amount of kibbles dispensed is 1/2 of the cup\nActual result - 1/2, the portion was dispensed in 2 increments with the interval. As there is no consistency again, let test this portion size one more time.\n \ntestcase thirteen\ndo repeat test case ten\ncheck the amount of kibbles is 1/2 of the cup\nActual result - a bit less than 1/2\n\n\ntestcase fourteen\ndo repeat test case eleven\ncheck the portion size is 1/2 of the cup\nActual result - a bit more than 1/2. This portion size looks more stable. The lunch isn't the smallest anymore.\n\ntestcase fiftee\ndo repeat test case twelve\ncheck portion size is 1/2 of the cup\nActual result - is l/2, a bit less of the exact amount. All three meals were served with almost correct portion size.\n\n\n- \n\n\n");
        mainPanel.add(new JScrollPane(textPane), BorderLayout.CENTER);
        setContentPane(mainPanel);

    }

    public static void main(String[] args) {
        CPerfJava7 frame;

        WindowListener exitListener = new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                Window window = e.getWindow();
                window.setVisible(false);
                window.dispose();
                System.exit(0);
            }
        };

        frame = new CPerfJava7();
        frame.addWindowListener(exitListener);
        frame.setPreferredSize(new Dimension(600, 400));
        frame.pack();
        frame.setVisible(true);

        try {
            // run it to warm it up
            frame.docu.doParse();
            frame.docu.doParse();
            frame.docu.doParse();
            frame.docu.doParse();
            frame.docu.doParse();
            frame.docu.doParse();
            // then do real measures
            totalTime = 0;
            for (int k=0; k<50; k++) {
                frame.docu.doParse();
            }
            System.err.println(" AVERAGE = " + (totalTime/50));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /*
    +----------------------------------------------------------------------+
    |                    Class: CPseudoHtmlDocument                        |
    +----------------------------------------------------------------------+
    */

    @SuppressWarnings("serial")
    public class CPseudoHtmlDocument extends DefaultStyledDocument {

        // +==============================================================+
        // | Attribute(s)                                                 |
        // +==============================================================+

        private final SimpleAttributeSet attrNormal  = new SimpleAttributeSet();
        private final SimpleAttributeSet attrSpecial = new SimpleAttributeSet();
        private final SimpleAttributeSet attrDefault = new SimpleAttributeSet();

        private int currentPos = 0;

        // +==============================================================+
        // | Constructor(s)                                               |
        // +==============================================================+

        public CPseudoHtmlDocument() {
            StyleConstants.setForeground(attrSpecial, Color.decode("0xC71D15"));StyleConstants.setBold(attrSpecial, true);
            StyleConstants.setForeground(attrDefault, new Color(150, 150, 0));
            StyleConstants.setBold(attrDefault, true);
        }

        // +==============================================================+
        // | Method(s)                                                    |
        // +==============================================================+

        public long getCurrentTimeMillis() {
            Calendar now = Calendar.getInstance();
            return now.getTimeInMillis();
        }
        public String getCurrentLogTime() {
            Calendar now = Calendar.getInstance();
            return calendarToPreciseTimeOfTheDay(now);
        }
        public String calendarToPreciseTimeOfTheDay (Calendar calendar) {
            SimpleDateFormat localSimpleDateFormat = new SimpleDateFormat("HH:mm:ss.S");
            return localSimpleDateFormat.format(calendar.getTime());
        }

        private void doParse() {
            long before = getCurrentTimeMillis();
            setCharacterAttributes(0, 3260, new SimpleAttributeSet(), true); // reset all the text to review (including the rewinded part) with normal attribute
            for (int i = 0; i <= 3260; i++) {
                currentPos = i;
                checkForKeyword(true);
            }
            long after = getCurrentTimeMillis();
            totalTime += (after-before);
            System.err.println("   -> time to parse: " + (after-before) + " ms");
        }

        private void checkForKeyword(boolean insert) {
            int preceedingStartTag, nextStopTag, preceedingPercentTag, nextPercentTag, preceedingSpace, nextSpace;
            char currentChar = ' ';
            try {
                currentChar = this.getText(currentPos, 1).charAt(0);
            } catch (BadLocationException e1) {
            }

            if (currentChar == '[') {
                nextStopTag = getNextStop(currentPos+1, true);
                setStyle(currentPos, nextStopTag); // check if this is a [xxx] we need to highlight

            } else if (currentChar == ']') {
                preceedingStartTag = getPreceedingStart(currentPos-1, true); // returns -1 if not found or another [, ], \n or space found first
                setStyle(preceedingStartTag, currentPos); // check if this is a [xxx] we need to highlight

            } else if (currentChar == '%') {
                nextPercentTag = getNextPercent(currentPos+1, true); // returns -1 if not found or \n or space found first
                if (nextPercentTag >= 0) {
                    // removed code
                }

            } else {
                // [xxx]
                preceedingStartTag = getPreceedingStart(currentPos-1, true); // returns -1 if not found or another ], \n or space found first
                nextStopTag = getNextStop(currentPos, true); // returns -1 if not found or another [, \n or space found first
                if (preceedingStartTag >=0 && nextStopTag >=0) {
                    setStyle(preceedingStartTag, nextStopTag);
                }

                // PARAMS
                preceedingPercentTag = getPreceedingPercent(currentPos-1, true); // returns -1 if not found or another [, ], \n or space found first
                nextPercentTag = getNextPercent(currentPos, true); // returns -1 if not found or another [, ], \n or space found first
                if (preceedingPercentTag >=0 && nextPercentTag >=0) {
                }

                // PDNL
                preceedingSpace = getPreceedingSpace(currentPos-1, true); // returns -1 if another [, ] or % found first, 0 if not found 
                nextSpace = getNextSpace(currentPos, true); // returns -1 if not found or another [, ] or % found first, length-1 if not found 
                if (preceedingSpace >=0 && nextSpace >=0) {
                    if (preceedingSpace == 0) preceedingSpace--;      // keyword at the very beginning of the text
                    setStyle(preceedingSpace+1, nextSpace-1);
                }
            }
        }

        private int getPreceedingStart(int offset, boolean strict) {
            while (offset >= 0) {
                try {
                    char charAt = this.getText(offset, 1).charAt(0);
                    if (charAt == '[') {
                        return offset;
                    } else if (charAt == ']' || charAt == '%' || charAt == ' ' || charAt == '\n' || charAt == '\r' || charAt == '\t') {
                        if (strict)
                            return -1;
                    }
                } catch (BadLocationException e) {
                    e.printStackTrace();
                }
                offset--;
            }
            if (strict)
                return -1;
            else
                return 0;
        }
        private int getNextStop(int offset, boolean strict) {
            while (true) {
                try {
                    char charAt = this.getText(offset, 1).charAt(0);
                    if (charAt == ']') {
                        return offset;
                    } else if (charAt == '[' || charAt == '%' || charAt == ' ' || charAt == '\n' || charAt == '\r' || charAt == '\t') {
                        if (strict)
                            return -1;
                    }
                } catch (BadLocationException e) {
                    if (strict)
                        return -1;
                    else
                        return offset-1;
                }
                offset++;
            }
        }
        private int getPreceedingPercent(int offset, boolean strict) {
            while (offset >= 0) {
                try {
                    char charAt = this.getText(offset, 1).charAt(0);
                    if (charAt == '%') {
                        return offset;
                    } else if (charAt == ' ' || charAt == '\n' || charAt == '\r' || charAt == '\t') {
                        if (strict)
                            return -1;
                    }
                } catch (BadLocationException e) {
                    e.printStackTrace();
                }
                offset--;
            }
            if (strict)
            return -1;
            else 
                return 0;
        }
        private int getNextPercent(int offset, boolean strict) {
            while (true) {
                try {
                    char charAt = this.getText(offset, 1).charAt(0);
                    if (charAt == '%') {
                        return offset;
                    } else if (charAt == ' ' || charAt == '\n' || charAt == '\r' || charAt == '\t') {
                        if (strict)
                            return -1;
                    }
                } catch (BadLocationException e) {
                    if (strict)
                        return -1;
                    else
                        return offset;
                }
                offset++;
            }
        }
        private int getPreceedingSpace(int offset, boolean strict) {
            while (offset >= 0) {
                try {
                    char charAt = this.getText(offset, 1).charAt(0);
                    if (charAt == ' ' || charAt == '\n' || charAt == '\r' || charAt == '\t') {
                        return offset;
                    } else if (charAt == '%' || charAt == '[' || charAt == ']') {
                        if (strict)
                            return -1;
                    }
                } catch (BadLocationException e) {
                    e.printStackTrace();
                }
                offset--;
            }
            return 0;
        }   
        private int getNextSpace(int offset, boolean strict) {
            while (true) {
                try {
                    char charAt = this.getText(offset, 1).charAt(0);
                    if (charAt == ' ' || charAt == '\n' || charAt == '\r' || charAt == '\t') {
                        return offset;
                    } else if (charAt == '%' || charAt == '[' || charAt == ']') {
                        if (strict)
                            return -1;
                    }
                } catch (BadLocationException e) {
                    return offset-1;
                }
                offset++;
            }
        }
        private void setStyle(int startTag, int stopTag) {
            if (startTag >= 0 && stopTag > 0 && stopTag > startTag) {
                int tagLength = stopTag-startTag+1;
                try {
                    String tag = this.getText(startTag, tagLength);
                    String s = tag.trim()/*.toLowerCase()*/;

                    if (s.equals("testcase") || s.equals("do") || s.equals("check") || s.equals("and")) {
                        setCharacterAttributes(startTag, tagLength, attrSpecial, true);

                    } else {
                        setCharacterAttributes(startTag, tagLength, attrNormal, true);
                    }
                } catch (BadLocationException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}

On my computer if I run it with Java 6, I get an average time of 60ms to run it.

If I run it with Java 7 it will take 460ms of so.

Am I the only one who noticed that? Thanks,

Edit: Here is the new program that proves the faulty method is DefaultStyledDocument.setCharacaterAttributes() in Java 7:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Window;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.util.Calendar;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.text.DefaultStyledDocument;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;

public class CPerfJava7_justSetCharacterAttributes extends JFrame {
    private static final long serialVersionUID = 1L;

    // +==============================================================+
    // | Attributes                                                   |
    // +==============================================================+
    JPanel mainPanel;
    public CPseudoHtmlDocument docu;

    // +==============================================================+
    // | Constructors                                                 |
    // +==============================================================+

    public CPerfJava7_justSetCharacterAttributes() {
        super("PerfJava7");
        docu = new CPseudoHtmlDocument();
        mainPanel = new JPanel(new BorderLayout());
        JTextPane textPane = new JTextPane();
        textPane.setDocument(docu);
        textPane.setText("testcase one\ndo program breakfast meal\ndo program time\ndo program portion size - 1/4 of the cup\nIt was easy to set up the time and portion size as directed in the manual\n\ndo collect kibbles from the bowl and measure the breakfast portion\ncheck the portion dispensed 1/4 cup of kibbles \nActual result - it was less than 1/4 cup. The wheel that rotates and dispenses\n the kibbles might haven't rotated completely.\n\ntestcase two\ndo program lunch meal\ndo program time\ndo leave the portion size the same - 1/4 of the cup\n\ncheck the portion dispensed 1/4 cup of kibbles\n do collect kibbles from the bowl and measure the lunch portion\nActual result was 1/20 of the cup, the wheel dispenser hardly released any kibbles\n\ntestcase three\n\ndo program dinner meal\ndo program time\ndo leave the portion size the same - 1/4 of the cup\n\ncheck the portion dispensed 1/4 cup of kibbles\n do collect kibbles from the bowl and measure the dinner portion\nActual result was  less than 1/4 cup\n\nWhy was the middle meal the smallest? Is lunch always the smallest?\nTo prove this hypothesis, repeat first three test cases again.\n\ntestcase four\ndo repeat test case one\nActual result - it was less 1/4 and the portion was dispensed in two increments.\n\ntestcase five\ndo repeat test case two\nActual result - 1/8 of the cup.\n\ntestcase six\ndo repeat test case three\nActual result - 1/2 cup! The wheel rotated almost twice. It looked like all\n kibbles which were not dispensed previously, got released.\n Lunch was still the smallest meal, but the dinner portion was twice of the size\n.All three tests must be repeated again.\n\ntestcase seven\ndo repeat test case one\nActual result - 1/4 of the cup. Great!\n\ntestcase eight\ndo repeat test case two\nActual result - 1/4 of the cup! It works perfectly! Why?\n\ntestcase nine\ndo repeat test case three\nActual result - 1/6, so the third meal was less than it should be. Is this size of the portion hard to dispense correctly? Let's try 1/2 of the cup\n\ntestcase ten\ndo program breakfast meal\ndo program time\ndo program 1/2 of the cup\ncheck the amount of kibbles dispensed is 1/2 of the cup\nActual result - 1/2 of the cup. Excellent. The wheel rotated twice as designed. In a minute it added 1/4 of the cup.\n\ntestcase eleven\ndo program lunch meal\ndo program time\ndo leave the portion size the same\ncheck the amount of kibbles dispensed is 1/2 of the cup\nActual result - 1/3 of the cup. Lunch is smaller again.\n\testcase twelve\ndo program dinner meal\ndo program time\ndo leave the portion size the same - 1/2 of the cup\ncheck the amount of kibbles dispensed is 1/2 of the cup\nActual result - 1/2, the portion was dispensed in 2 increments with the interval. As there is no consistency again, let test this portion size one more time.\n \ntestcase thirteen\ndo repeat test case ten\ncheck the amount of kibbles is 1/2 of the cup\nActual result - a bit less than 1/2\n\n\ntestcase fourteen\ndo repeat test case eleven\ncheck the portion size is 1/2 of the cup\nActual result - a bit more than 1/2. This portion size looks more stable. The lunch isn't the smallest anymore.\n\ntestcase fiftee\ndo repeat test case twelve\ncheck portion size is 1/2 of the cup\nActual result - is l/2, a bit less of the exact amount. All three meals were served with almost correct portion size.\n\n\n- \n\n\n");
        mainPanel.add(new JScrollPane(textPane), BorderLayout.CENTER);
        setContentPane(mainPanel);

    }

    public static void main(String[] args) {
        CPerfJava7_justSetCharacterAttributes frame;

        WindowListener exitListener = new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                Window window = e.getWindow();
                window.setVisible(false);
                window.dispose();
                System.exit(0);
            }
        };

        frame = new CPerfJava7_justSetCharacterAttributes();
        frame.addWindowListener(exitListener);
        frame.setPreferredSize(new Dimension(600, 400));
        frame.pack();
        frame.setVisible(true);

        try {
            // run it to warm it up
            frame.docu.doParse();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /*
    +----------------------------------------------------------------------+
    |                    Class: CPseudoHtmlDocument                        |
    +----------------------------------------------------------------------+
    */

    @SuppressWarnings("serial")
    public class CPseudoHtmlDocument extends DefaultStyledDocument {

        private final SimpleAttributeSet attrDefault = new SimpleAttributeSet();

        public CPseudoHtmlDocument() {
            StyleConstants.setForeground(attrDefault, new Color(150, 150, 0));
            StyleConstants.setBold(attrDefault, true);
        }

        public long getCurrentTimeMillis() {
            Calendar now = Calendar.getInstance();
            return now.getTimeInMillis();
        }

        private void doParse() {
            long before = getCurrentTimeMillis();
            for (int k=0; k<5; k++) {
                for (int i = 0; i <= 3260; i+=1) {
                    setStyle(i, i+1);
                }
            }
            long after = getCurrentTimeMillis();
            System.err.println("   -> time to parse: " + (after-before) + " ms");
        }

        private void setStyle(int startTag, int stopTag) {
            int tagLength = stopTag-startTag+1;
            setCharacterAttributes(startTag, tagLength, attrDefault, true);
        }
    }

}

On my computer:

Java 6: ~1 sec

Java 7: ~4.5 sec

Any way to make it work in Java 7 as fast as in Java 6? Thanks,

EDIT: here is a workaround that fixes the perf. issue with Java 7 (I did it on insertString() and remove()):

public void remove(int offset, int length) throws BadLocationException {
    // start by removing normally the string using the default styled document
    try {
        super.remove(offset, length);
    } catch (Exception e) {
        e.printStackTrace();
    }

    int caretPosition = textPane.getCaretPosition();
    textPane.setDocument(new DefaultStyledDocument());

    // DO HERE THE TIME-CONSUMING TASKS THAT APPLY SOME STYLES ON THE CURRENT DOCUMENT

    textPane.setDocument(this);
    textPane.setCaretPosition(caretPosition);
}
Maike
  • 148
  • 6
  • Did you compile it both with java6 and java7 jdk or did you just change the runtime enviroment? – Lukas Eichler Nov 09 '13 at 15:35
  • I noticed this on my real program first which is compiled for Java6 to be compatible with both 6 and 7. But now I'm running this simple code directly from eclipse just selecting a different JRE from the preferences. – Maike Nov 09 '13 at 15:37
  • 1
    Can you reduce the test case to something we can test? Which specific versions of Java 6 and 7 did you test? For example the internal implementation of String was changed in 1.7.0_06 with many performance implications http://java-performance.info/changes-to-string-java-1-7-0_06/ Have you tried using a profiler to find the difference? – Joni Nov 09 '13 at 15:39
  • You can execute this code. It took me quite some time to extract something runnable by anyone. I tested it with JRE 1.6.0_25 64 bits and JRE 1.7.0_45 64 bits. I didn't use any profiler as I'm not sure how to use it... – Maike Nov 09 '13 at 15:44
  • The import statements are missing.. – Joni Nov 09 '13 at 15:45
  • Ooopss... sorry. Corrected now. – Maike Nov 09 '13 at 15:48
  • 2
    +1 I have an very interested output too JDK1.6_037 is average 167milis and JDK1.7._040 is 3938, you have to test & use JProfiler, – mKorbel Nov 09 '13 at 15:54
  • I would start and remove the GUI parts and see if the difference is still there. However a profiler should reveal what is taking so much longer. – Thomas Jungblut Nov 09 '13 at 15:55
  • I've just run your code with Java 6 (jdk-6u45) and Java 7 (jdk-7u45) and got different results: Java 6: 100-120 ms, Java 7: 50-60 ms. – isnot2bad Nov 09 '13 at 16:16
  • Something interesting: I moved all the processing outside of the StyledDocument and commented the setCharacterAttributes() call and now I have exactly the same performances with JRE6 and JRE7... So the perf issue with Java 7 is about StyledDocument and/or setCharacterAttributes() – Maike Nov 09 '13 at 16:31
  • New experience: took the original code posted here and commented all setCharacterAttributes() ==> now I get the same measures. conclusion: Java 7's setCharacterAttributes() implementation is much slower than Java 6 but why? – Maike Nov 09 '13 at 16:34
  • Ok, I've run a profiler to understand where are the differences in java 6 and java 7 executing the same code. Here are my conclusion: Java 7 is faster than Java 6 on many things but where Java 7 sucks really is on javax.swing.RepaintManager$ProcessingRunnable.run() AND more importantly I guess documentStyled.setStyle(int, int). it really looks like setStyle() is the faulty method in my code – Maike Nov 09 '13 at 17:24
  • Ok, I've got now a much shorter/simpler program that proves that DefaultStyledDocument.setCharacterAttributes() in Java 7 is crappy. what's the best to publish this new code? do I have to answer my own question? or edit the original post? – Maike Nov 09 '13 at 17:42
  • added the program at the end of the original post... – Maike Nov 09 '13 at 18:08
  • Are you considering JIT'ed code, inlined methods,classes, etc? Java 7 had a lot of improvements to the JVM, so it's possible your one-off test case does not invoke the proper optimizations in the JVM's optimizing-compiler. Also, JavaFX is the replacement for Swing/AWT - it's not only faster to write, but runs faster, is more flexible, etc. And you can use CSS to style it... – SnakeDoc Nov 09 '13 at 22:06

3 Answers3

4

I think the problem not the Document but difference in view structure layout.

Try to create a Document without JTextPane and apply all the styles.

Break spots calculations were introduced in java7 to define where view can be wrapped. There is a bug in layout see Strange text wrapping with styled text in JTextPane with Java 7

Guess the same happens with simple layout.

You can try to play with the views ParagraphView and LabelView and see how different changes in layout extremly change performance.

Community
  • 1
  • 1
StanislavL
  • 56,971
  • 9
  • 68
  • 98
  • If I disconnect the document from the JTextPane and do exactly the same (applying a lot of styles). The performance is just great. I'm moving from 4500ms to 400ms. – Maike Nov 10 '13 at 10:03
  • When one applies many differents styles on different sections in a long text, it looks like it's not the applying of those styles on the document that takes the huge time in Java 7 but the rendering in the text pane (layout). Hence the following idea: is it possible to "disconnect" the document from the textpane during the style applying in the document and reapplying it later? but this would need to be done from the document itself... This would fix the performance problem and would be a not so dirty solution. But I did not succeed to do so yet – Maike Nov 10 '13 at 10:27
  • OK I got my workaround !!! :) So in my opinion, there is definitly a huge performance problem in Java 7 when it's about rendering some applied styled (there was absolutely no problem in Java 6). My workaround consist in setting artificially teh text componenent to the default StyledDocument before applying a lot of style in different section in the StyledDocument then reapplying to the current StyledDocument. – Maike Nov 10 '13 at 10:56
  • You can create a Document, set all the styles and texts and only after that call jTextPane.setDocument(theDocument);. Then rendering will be called just once. – StanislavL Nov 11 '13 at 05:19
2

As suggested by this link, perhaps it can be interesting to replace setCharacterAttributes with getHighlighter().addHighlight(). It may be faster and perhaps the difference between Java version be reduced a lot. I can not test it since it will require too much change to the code.

sebtic
  • 198
  • 1
  • 6
  • Quick feedback: using the highlighter instead of setCharacter Attributes is millions time faster! 60ms instead of 4300ms for the same test. Need to check now how I can change the font foreground color, bold etc. will let you know... – Maike Nov 09 '13 at 22:50
  • the only thing is it can just paint the background of a text section not change the foreground color, bold etc. of the text itself... – Maike Nov 09 '13 at 23:04
0

I see that given code snippets is using SimpleDateFormat. Calendar.GetInstance and SimpleDateFormat are known performance issue in Java 7, specially when multiple threads are being run. Try changing SimpleDateFormat to JodaTime or change your code to use TimeStamp (which is long format) and test once.