יום שלישי, 18 באוקטובר 2011

ארבעה דברים טובים שנוספו עם Java 7

כבר נשמעה הרבה ביקורת, על כמה מעט נוסף במשך חמש וטיפה שנות הפיתוח (או יותר נכון: דיונים) - של ג'אווה 7.
אם גלעד שליט ידע ג'אווה לפני שנחטף - אז הוא חזר לסביבה מוכרת וחסרת הפתעות.
סיכמתי כמה דברים חיוביים לאלו שיעברו ל Java 7, במקרה או בכוונה, ששווה להכיר:

פחות צורך במשמעת עצמית ב Exception Handling
Java נבנתה בצורה שתקל על המפתח להמנע משגיאות, הפחזבל (Garbage Collector) חסך מיליוני שנות פרך של debug וחיפוש אחר זליגות זכרון. בכל זאת, חברה שלא אנקוב בשמה, שילמה 17 מיליון דולר השתתפות בנזקים של הלקוח בעקבות הבאג הבא (חפשו אותו):

  Connection conn = null;
  Statement stmt = null;
  try {
    conn = connectionPool.getConnection();
    stmt = conn.createStatement();
    // Do the lookup logic
    // return a list of results
    } finally {
      if (stmt != null) {
        stmt.close();
      }
      if (conn != null) {
        conn.close();
    }
  }

אני מניח שהבאג לא צועק, הקוד נראה במבט ראשון מסודר ונכון. אבל מה - אם ()stmt.close יזרוק exception (בגלל מנגנון ה keep alive  של Oracle?), ה connection לא יתנקה וכשפעולה זו תחזור שוב ושוב, Oracle יסרב לקבל עוד קריאות. במערכת Mission Critical המשמעות היא לבצע restart לבסיס הנתונים כל מספר דקות, חוסר יכולת לעבוד, יום בטלה לעובדים והפסד אדיר ללקוח.

הפתרון הוא לעטוף ב try-catch נוספים (ה catch יישאר כנראה ריק) את קריאות ה ()close של stmt ו conn. זה היה ה best practice שדרשנו ממפתחים לעשות, שוב ושוב, וברגשות מעורבים.

איך מוודאים שבקשה זו תיושם בפועל? ביצוע תחוף של Code Reviews, הגדרת המודעות בקרב המפתחים ובארגון שעובד נכון - בניית Connection Pool שיודע לזרוק Connections בשעת לחץ.
בג'אווה 7 ניתן לכתוב את הקוד הבא:

  try (Connection conn = connectionPool.getConnection();
       Statement stmt = conn.createStatement()) {
    // Do the lookup logic
    // return a list of results
    } catch {
      // respond to exception
    }

  }

שדואג ש conn ו stmt ייסגרו, אם לא שווים ל null ולמרות exceptions שיכולים להזרק. הוא מקביל לקוד הארוך והמכוער שתארתי למעלה. נחמד!


פחות חזרה חזרה חזרה ב catch statement
תופסים שלושה סוגי Exception ורוצים לעשות אותו הדבר?
מעכשיו כתבו:


} catch (IOException | SQLException | SQLStatementException ex) {
  log.warn(ex);
  throw ex;
}
המשתנה ex יתייחס לסוג ה Exception (מתוך השלושה) שנזרק. יופי.


Performance Profiling: שחרור VistualVM
אם תחפשו בתוך ספריית Bin ב JDK שהורדתם, תגלו שיושב לו שם Profiler בשם VisualVM. מלבד כך שהוא חינמי, מתחבר לכל ה IDEs החשובים (עם plugin) ויש לו ממשק משתמש נחמד, הוא מספק נקודת איזון מאוד מוצלחת של פשוטות מול עוצמה. הוא ידידותי - אבל גם אינו צעצוע. הוא יכול להספיק לרוב משימות ה profiling הנפוצות, גם בתוך tomcat, ואינו דורש הגדרות מיוחדות לJVM או לשרת שאתם עובדים איתו.

כדי לוודא שאתם משתמשים בגרסה החדשה ביותר חפשו כאן. בזמן כתיבת הפוסט הגרסה העדכנית היא 1.3.3.

נ.ב. - אופס, תפסתם אותי. VisualVM מסופק החל ממרץ כבר ב JDK 1.6 ואינו חידוש של Java 7. בכלל הוא יכול לעבוד (תחת מגבלות) עם אפילקציות שקומפלו ב JDK1.4. הוספתי אותו לכאן מכיוון שהוא תוספת מכובדת ל JDK שקל לפספס בגלל שיצא ב update ולא ב major release.


גרסה חדשה - מספר חדש
סימן חיים, ג'אווה עדיין קיימת! זה לא דבר גדול אבל, (סרקזם קל) זה בהחלט אחד השינויים הבולטים בגרסה שקיבלנו.


בהצלחה!

6 תגובות:

  1. אין לי כל כך נסיון ב-java אבל אם אני משליך את זה על C# ו-C++ אז הבעיה כאן שהמתודה close יכולה לזרוק exception
    אסור שמתודה לשחרור משאבים תוכל לזרוק שגיאה

    השבמחק
  2. נקודה מעניינת!
    אני מסכים שזה תכנון טוב יותר, אך ב Java ניסו לעשות הכל מפורש ודווקא למקרים אלו הוסיפו CheckedException, כאלו שהקומפיילר יחייב אותך לתפוס. הוסיפו קצת יותר מדי checked exceptions כך שהפכו למטרד וההתנהגות הסבירה של המפתח הוא להתעלם מהן.
    סה"כ אני מסכים שזה תכנון לא מוצלח של שפת Java.

    השבמחק
  3. VisualVM אכן כבר קיים הרבה זמן, בהתחלה כתוכנה חיצונית ל-JDK ואח"כ כחלק ממנו. אני אישית מעדיף לעבוד עם ההתקנה העצמאית, שכן היא בד"כ יותר מתקדמת. בנוסף, כדאי תמיד להתקין את הפלעאינים כדי לקבל עוד הרבה יכולות מוניטורינג וגישה למה שקורה ב- VM.

    לא צריך להגדיר כלום על ה-VM אם מריצים את ה- VisualVM על אותה מכונה, אם רוצים לנטר מכונה אחרת, צריך להוסיף הגדרות ל- JAVA כפי שמתואר כאן http://ow.ly/7gr2s

    השבמחק
  4. I would say that G1 garbage collector that comes in Java 7 instead of concurrent mark sweep is important enough to be mentioned

    השבמחק
  5. תודה על ההארה!
    האמת שעדיין לא ממש גיבשתי לגבי הG1 דעה. רק להסביר: G1 הוא מנוע Garbage Collector חדש שהוא ה Default ב Java 7.
    הרעיון מאחוריו, עד כמה שאני יודע, הוא שיפור מקביליות לעבודה על הרבה cores (דבר שהפך נפוץ יותר בחמש שנים האחרונות) + היכולת לשלוט בזמני ההפסקה של full GC (עד היום, כאשר יש full GC ה JVM היה עוצר עד שה full GC נגמר). לשרת עם הרבה זיכרון (עשרות GB) - עצירה זו יכולה הייתה להיות אפילו דקות, זמן בלתי אפשרי. היו כל מיני פתרונות 3rd Party לבעיה - אבל היא כנראה הפכה מספיק נפוצה בכדי להכנס לסטנדרט. יש פוסטים שמהללים את ה G1 (קראתי איזה השוואה די דמיונית שמדברת על 25% שיפורי throughput של המערכת, נשמע לי מאוד נקודתי) ויש פוסטים שאומרים שהוא בעייתי, בעיקר על חומרות מסוימות וחסר לו tuning. אני זוכר שה JVM ב 1.6 הציג שיפורים משמעותיים ובאופן קונסיסטנטי. האם מדובר על משהו דומה או שיפור הרבה יותר קטן?

    אם יש למישהו מידע נוסף / מדויק - אשמח לשמוע!

    השבמחק
  6. I agree with you that G1 is not a panacea. I like personally its concept - make garbage collection effects more determenistic. Not sure that making G1 a default GC is a good decision (look at that article - http://nerds-central.blogspot.com/2011/11/comparing-java-7-garbage-collectors.html)

    השבמחק