Унтаж буй баавгай

Ичих үедээ баавгайн зүрхний цохилт удааширч, биеийн халуун нь буурч, үхсэн мэт болдог онцлогтой. Ичээнээсээ дөнгөж гарсан өлсгөлөн баавгайтай тааралдвал маш аюултай.
—Анчдын ярианаас

Нилээд хэдэн сарын өмнө би нэг системийн хөгжүүлэлтийн төсөлд оролцсон юм. Товчхондоо бол сүлжээний хяналтын програм хангамж байлаа. 24x7x365 цаг зогсолтгүй ажиллах ёстой учраас хурд хүчнээс гадна найдвартай байдал болон тогтвортой байдлыг нилээд өндөр түвшинд шаардаж байлаа. Бид ч чадлынхаа хирээр хийгээд, хүлээлгэж өгөөд, сурилуулж ажиллуулаад бүх юм жин тан болов. Гэтэл гурван сарын дараа гэнэт нөгөө систем чинь зогсчихсон гэсэн түгшүүрт мэдээ ирдэг байгаа. Ашгүй хэрэглэгчдэд хохирол учраагүй, системийг тэр дор нь ахиж ачаалаад одоо зүв зүгээр ажиллаж байна гэхийн? Ингээд зогссон шалтгааныг олохоор баахан лог файл шүүрдэж элдэв зүйл асууж тодруулан нягталсан боловч учрыг бүрэн тодруулчихаар зүйл олсонгүй. Ингээд системийн үндсэн хэсгийг Жава хэл дээр хийсэн байсан тул дараах хоёр таамаг гаргасан нь: 1) бидний програм дотор ямар нэг алдаа байна, эсвэл 2) жава хөрсөнд байгаа ямар нэг алдаанаас болж систем зогссон байна.

Эхний таамгын мөрөөр бид програм дотроо зарим нэг чангаруулалт хийж өгөв. Юу гэхээр удаан хугацааны дараа алдаа гарч магадгүй гэсэн зүйлээ илүү найдвартай болгож refactoring хийсэн хэрэг. Хоёр дахь таамаг бол биднээс "бараг" хамааралгүй тул "харзнахаас" аргагүй байлаа. Өмнөх өгүүлбэрт "бараг", "харзнахаас" гэсэн үгсийг яагаад онцолсныг одоо тайлбарласу. Ингээд гол хэсэгтээ оръё.

Доор java.util.Arrays классын binarySearch методыг харуулав.

public static int binarySearch(byte[] a, byte key) {
int low = 0;
int high = a.length-1;

while (low <= high) {      int mid = (low + high) >> 1;
    byte midVal = a[mid];

    if (midVal < low =" mid"> key)
        high = mid - 1;
    else
        return mid; // key found
}
return -(low + 1);  // key not found.
}

Саявтархан Жава хөрсийг бүрэлдүүлэхэд голлох үүрэг гүйцэтгэсэн инженерүүдийн нэг

int mid = (low + high) / 2;

буюу арай өөрөөр бичвэл,

int mid = (low + high) >> 1;

байх нь ойлгомжтой. Гэвч хэрвээ энэ хоёрын ядаж аль нэг нь integer тооны дээд хязгаарт (231 - 1) маш ойрхон тохиолдолд яах вэ? Компьютер эхлээд нийлбэрийг бодоод түүнийгээ 2т хуваана. Гэтэл хоёр тооны нийлбэр integer тооны дээд хязгаараас хэтэрчихээр хасах тоо болоод эргэчихдэг, ингээд хоёрт хуваахаар хасах тоо л гарна. Алдаа!!! Энэ алдаа жава хөрсөнд саяхныг болтол байсан гээд бод доо. Итгэхгүй бол та дараах кодыг ажиллуулаад үзээрэй. Дараах жишээ кодод (230 + 1) ширхэг гишүүнтэй байт массив үүсгээд хамгийн сүүлийн гишүүний утгыг 1(бусад гишүүд заяамал утгаараа=0) гэж цэнэглэж өгч байгаа. binarySearch энэ тохиолдолд зүгээр ажиллах боловч, (230 + 2) гишүүнтэй массивийн хувьд ArrayIndexOutOfBoundsExceptionалдаа заана.

public class ArraysTest {
   public static final int DEAD_LIMIT = 1 + (Integer.MAX_VALUE / 2);
   private int length;
   private byte array[];
   private byte key = (byte) 1;

   public ArraysTest(int len) throws java.lang.OutOfMemoryError {
       this.length = len;
       array = new byte[len];
       array[len - 1] = key;
   }

   public void runBinarySearch() {
       System.out.println(" Massiwiin elementiin too :" + length);
       int pos = Arrays.binarySearch(array, key);
       System.out.println(" Oldson bairlal :" + pos);
   }

   public static void main(String[] args) {
       try {
           System.out
                   .println("1-r test: (array.length==Integer.MAX_VALUE/2+1)");

           ArraysTest test = new ArraysTest(DEAD_LIMIT);
           test.runBinarySearch();

           System.out
                   .println("2-r test: (array.length==Integer.MAX_VALUE/2+2)");
           // Uymiig yaaj medehew sanah oig chuluulnuu ;)
           test = null;
           System.gc();

           test = new ArraysTest(DEAD_LIMIT + 1);
           test.runBinarySearch();

       } catch (java.lang.OutOfMemoryError e) {
           System.out.println("-Sanah oi hureltsehgvi baina.");
           System.out.println("-1GB aas deesh sanah oi heregtei tul,");
           System.out.println("JVM -iig daraah parametrtei ajilluul:");
           System.out.println("java -Xms1150M -Xmx1150M ArraysTest");
       }
   }
}

Дээрх кодыг ArraysTest.java файлд хадгалаад командын мөр дээр:

javac ArraysTest.java
java -Xms1150M -Xmx1150M ArraysTest

гэж ажиллуулахад дараах үр дүн харагдах ёстой.

1-r test: (array.length==Integer.MAX_VALUE/2+1)
Massiwiin elementiin too :1073741824
Oldson bairlal :1073741823
2-r test: (array.length==Integer.MAX_VALUE/2+2)
Massiwiin elementiin too :1073741825
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: -1073741824

 at java.util.Arrays.binarySearch(Unknown Source)
 at ArraysTest.runBinarySearch(ArraysTest.java:17)
 at ArraysTest.main(ArraysTest.java:34)

Энэхүү алдаа мэдээж засагдсан бөгөөд засвар нь ердөө ганцхан тэмдэгт л өөрчилсөн болно.

int mid = (low + high) >> 1;

Дээрх мөрний шилжүүлэх операторыг тэмдэг хадгалахгүй шилжүүлэх оператор болгоход хангалттай.

int mid = (low + high) >>> 1;

Дүгнэж хэлэхэд аливаа кодыг зуун хувь бүрэн дүүрэн тэстлэнэ гэж байхгүй ажээ. Энэ бичлэгийн эхэнд дурдсан системийн зогссон шалтгаан ч иймэрхүү зүйл байсан байж болох. Цаг хугацаанд найдаад хавар болж баавгайг ичээнээсээ өөрөө гарахыг хүлээвэл өлсгөлөн баавгай хэрэг тарьж амжих нь дамжиггүй. Харин нөр хөдөлмөр, хичээл зүтгэл гаргаж чадаж гэмээнэ араатанг ичээндээ унтаж байхад нь олж номхотгох учиртай буюу.

Санал болгох ном:

Холбоос: