आउटपुट -1 लूप में स्लैश बन जाता है


54

हैरानी की बात है, निम्नलिखित कोड आउटपुट:

/
-1

कोड:

public class LoopOutPut {

    public static void main(String[] args) {
        LoopOutPut loopOutPut = new LoopOutPut();
        for (int i = 0; i < 30000; i++) {
            loopOutPut.test();
        }

    }

    public void test() {
        int i = 8;
        while ((i -= 3) > 0) ;
        String value = i + "";
        if (!value.equals("-1")) {
            System.out.println(value);
            System.out.println(i);
        }
    }

}

मैंने यह निर्धारित करने के लिए कई बार कोशिश की कि यह कितनी बार होगा, लेकिन, दुर्भाग्य से, यह अंततः अनिश्चित था, और मैंने पाया कि -2 का आउटपुट कभी-कभी एक अवधि में बदल जाता है। इसके अलावा, मैंने लूप और आउटपुट -1 को भी बिना किसी समस्या के हटाने का प्रयास किया। मुझे कौन बता सकता है क्यों?


JDK संस्करण की जानकारी:

HopSpot 64-Bit 1.8.0.171
IDEA 2019.1.1

2
टिप्पणियाँ विस्तारित चर्चा के लिए नहीं हैं; इस वार्तालाप को बातचीत में स्थानांतरित कर दिया गया है ।
सैमुअल एलवाईई

जवाबों:


36

यह मज़बूती से पुन: पेश किया जा सकता है (या पुन: प्रस्तुत नहीं किया जा सकता है, इस पर निर्भर करता है कि आप क्या चाहते हैं) openjdk version "1.8.0_222"(मेरे विश्लेषण में इस्तेमाल किया गया ) के साथ , 12.0.1ओपनजेडीके (ओलेकेंडर पायरोव के अनुसार) और ओपनजेडके 13 (कार्लोस हेबरगर के अनुसार)।

मैंने -XX:+PrintCompilationदोनों व्यवहार प्राप्त करने के लिए पर्याप्त समय के साथ कोड चलाया और यहां अंतर हैं।

छोटी गाड़ी कार्यान्वयन (उत्पादन प्रदर्शित करता है):

 --- Previous lines are identical in both
 54   17       3       java.lang.AbstractStringBuilder::<init> (12 bytes)
 54   23       3       LoopOutPut::test (57 bytes)
 54   18       3       java.lang.String::<init> (82 bytes)
 55   21       3       java.lang.AbstractStringBuilder::append (62 bytes)
 55   26       4       java.lang.AbstractStringBuilder::ensureCapacityInternal (27 bytes)
 55   20       3       java.lang.StringBuilder::<init> (7 bytes)
 56   19       3       java.lang.StringBuilder::toString (17 bytes)
 56   25       3       java.lang.Integer::getChars (131 bytes)
 56   22       3       java.lang.StringBuilder::append (8 bytes)
 56   27       4       java.lang.String::equals (81 bytes)
 56   10       3       java.lang.AbstractStringBuilder::ensureCapacityInternal (27 bytes)   made not entrant
 56   28       4       java.lang.AbstractStringBuilder::append (50 bytes)
 56   29       4       java.lang.String::getChars (62 bytes)
 56   24       3       java.lang.Integer::stringSize (21 bytes)
 58   14       3       java.lang.String::getChars (62 bytes)   made not entrant
 58   33       4       LoopOutPut::test (57 bytes)
 59   13       3       java.lang.AbstractStringBuilder::append (50 bytes)   made not entrant
 59   34       4       java.lang.Integer::getChars (131 bytes)
 60    3       3       java.lang.String::equals (81 bytes)   made not entrant
 60   30       4       java.util.Arrays::copyOfRange (63 bytes)
 61   25       3       java.lang.Integer::getChars (131 bytes)   made not entrant
 61   32       4       java.lang.String::<init> (82 bytes)
 61   16       3       java.util.Arrays::copyOfRange (63 bytes)   made not entrant
 61   31       4       java.lang.AbstractStringBuilder::append (62 bytes)
 61   23       3       LoopOutPut::test (57 bytes)   made not entrant
 61   33       4       LoopOutPut::test (57 bytes)   made not entrant
 62   35       3       LoopOutPut::test (57 bytes)
 63   36       4       java.lang.StringBuilder::append (8 bytes)
 63   18       3       java.lang.String::<init> (82 bytes)   made not entrant
 63   38       4       java.lang.StringBuilder::append (8 bytes)
 64   21       3       java.lang.AbstractStringBuilder::append (62 bytes)   made not entrant

सही रन (कोई प्रदर्शन नहीं):

 --- Previous lines identical in both
 55   23       3       LoopOutPut::test (57 bytes)
 55   17       3       java.lang.AbstractStringBuilder::<init> (12 bytes)
 56   18       3       java.lang.String::<init> (82 bytes)
 56   20       3       java.lang.StringBuilder::<init> (7 bytes)
 56   21       3       java.lang.AbstractStringBuilder::append (62 bytes)
 56   26       4       java.lang.AbstractStringBuilder::ensureCapacityInternal (27 bytes)
 56   19       3       java.lang.StringBuilder::toString (17 bytes)
 57   22       3       java.lang.StringBuilder::append (8 bytes)
 57   24       3       java.lang.Integer::stringSize (21 bytes)
 57   25       3       java.lang.Integer::getChars (131 bytes)
 57   27       4       java.lang.String::equals (81 bytes)
 57   28       4       java.lang.AbstractStringBuilder::append (50 bytes)
 57   10       3       java.lang.AbstractStringBuilder::ensureCapacityInternal (27 bytes)   made not entrant
 57   29       4       java.util.Arrays::copyOfRange (63 bytes)
 60   16       3       java.util.Arrays::copyOfRange (63 bytes)   made not entrant
 60   13       3       java.lang.AbstractStringBuilder::append (50 bytes)   made not entrant
 60   33       4       LoopOutPut::test (57 bytes)
 60   34       4       java.lang.Integer::getChars (131 bytes)
 61    3       3       java.lang.String::equals (81 bytes)   made not entrant
 61   32       4       java.lang.String::<init> (82 bytes)
 62   25       3       java.lang.Integer::getChars (131 bytes)   made not entrant
 62   30       4       java.lang.AbstractStringBuilder::append (62 bytes)
 63   18       3       java.lang.String::<init> (82 bytes)   made not entrant
 63   31       4       java.lang.String::getChars (62 bytes)

हम एक महत्वपूर्ण अंतर देख सकते हैं। सही निष्पादन के साथ हम test()दो बार संकलन करते हैं । एक बार शुरुआत में, और एक बार फिर बाद में (संभवतः क्योंकि जेआईटी नोटिस करता है कि विधि कितनी गर्म है)। बग्गी में निष्पादन 5 बार test()संकलित (या विघटित) होता है।

इसके अतिरिक्त, के साथ चल रहा -XX:-TieredCompilationहै (जो या तो व्याख्या या का उपयोग करता है C2) या के साथ -Xbatch(समांतर है, जो मुख्य थ्रेड में चलाने के लिए संकलन बलों के बजाय), उत्पादन कर रहा है की गारंटी और सामान का एक बहुत बाहर 30000 पुनरावृत्तियों प्रिंट के साथ है, तो C2संकलक लगता है अपराधी होना। इसके साथ चलने की पुष्टि की जाती है -XX:TieredStopAtLevel=1, जो निष्क्रिय करता है C2और उत्पादन नहीं करता है (स्तर 4 पर रोकना बग को फिर से दिखाता है)।

सही निष्पादन में, विधि पहले स्तर 3 संकलन के साथ संकलित की जाती है , फिर बाद में स्तर 4 के साथ।

छोटी गाड़ी के निष्पादन में, पिछले संकलन समाप्त हो गए हैं ( made non entrant) और इसे फिर से 3 स्तर पर संकलित किया गया है (जो कि C1, पिछले लिंक को देखें)।

तो यह निश्चित रूप से एक बग है C2, हालांकि मुझे पूरी तरह से यकीन नहीं है कि क्या तथ्य यह है कि यह स्तर 3 संकलन पर वापस जा रहा है, इसे प्रभावित करता है (और क्यों यह स्तर 3 पर वापस जा रहा है, इसलिए अभी भी कई अनिश्चितताएं हैं)।

आप खरगोश छेद में और भी गहराई तक जाने के लिए निम्नलिखित कोड के साथ विधानसभा कोड उत्पन्न कर सकते हैं ( विधानसभा मुद्रण को सक्षम करने के लिए यह भी देखें )।

java -XX:+PrintCompilation -Xbatch -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly LoopOutPut > broken.asm

इस बिंदु पर, मैं कौशल से बाहर निकलने के लिए शुरू कर रहा हूं, पिछले संकलित संस्करणों को छोड़ दिए जाने पर छोटी गाड़ी का व्यवहार प्रदर्शित होना शुरू हो जाता है, लेकिन मेरे पास जो थोड़ा सा विधानसभा कौशल है वह 90 के दशक का है, इसलिए मैं किसी को मेरे मुकाबले इसे ले जाने की अनुमति देता हूं। यहां से।

यह संभावना है कि इस बारे में पहले से ही एक बग रिपोर्ट है, क्योंकि किसी और के द्वारा ओपी को कोड प्रस्तुत किया गया था, और जैसा कि सभी कोड सी 2 बग के साथ नहीं है । मुझे आशा है कि यह विश्लेषण दूसरों के लिए उतना ही जानकारीपूर्ण रहा होगा जितना कि यह मेरे लिए था।

जैसा कि आदरणीय अपांगिन ने टिप्पणियों में बताया, यह एक हालिया बग है । सभी इच्छुक और सहायक लोगों के लिए बहुत कुछ है :)


मुझे यह भी लगता है C2- जेनिटवॉच कोड (और इसे समझने की कोशिश की गई है) को JitWatch का उपयोग करके देखा है - C1उत्पन्न कोड अभी भी बायटेकोड जैसा दिखता है, C2पूरी तरह से अलग है (मैं i8 के साथ आरंभीकरण का पता नहीं
लगा सका

आपका उत्तर बहुत अच्छा है, मैंने कोशिश की, सी 2 को अक्षम करें, परिणाम सही है। हालांकि, सामान्य तौर पर, इन मापदंडों में से अधिकांश परियोजना में डिफ़ॉल्ट हैं, हालांकि वास्तविक परियोजना में उपरोक्त कोड नहीं होगा, लेकिन इसके समान कोड होने की संभावना है, अगर परियोजना समान कोड का उपयोग करती है, तो यह वास्तव में भयानक है
okali

1
@ यूजीन यह काफी मुश्किल भरा रहा है, मुझे यकीन था कि यह ग्रहण संकलक बग या इसी तरह का कुछ होने जा रहा है ... और मैं इसे पहले भी पुन: पेश नहीं कर सका था
कायमन

1
@ कायमन मान गई। आपके द्वारा किया गया विश्लेषण बहुत अच्छा है, यह इतना अधिक होना चाहिए कि अपंगिन को यह समझाने और ठीक करने के लिए पर्याप्त हो। क्या शानदार सुबह है ट्रेन पर!
यूजीन

7
मैंने इस विषय को केवल आकस्मिक रूप से देखा। यह सुनिश्चित करने के लिए कि मैं प्रश्न देखता हूं, @mentions का उपयोग करें या #jvm टैग जोड़ें। अच्छा विश्लेषण, बीटीडब्ल्यू। यह वास्तव में एक C2 कंपाइलर बग है, जिसे कुछ दिन पहले ही ठीक किया गया है - JDK-8231988
अपांगिन

4

यह ईमानदारी से बहुत अजीब है, क्योंकि उस कोड को तकनीकी रूप से कभी भी उत्पादन नहीं करना चाहिए क्योंकि ...

int i = 8;
while ((i -= 3) > 0);

... हमेशा में परिणाम चाहिए iकिया जा रहा है -1(8 - 3 = 5, 5 - 3 = 2; 2 - 3 = -1)। यहां तक ​​कि निराई भी है कि यह मेरी आईडीई के डिबग मोड में कभी भी आउटपुट नहीं करता है।

दिलचस्प है, जिस क्षण मैं रूपांतरण से पहले एक चेक जोड़ता हूं String, फिर कोई मुद्दा नहीं ...

public void test() {
  int i = 8;
  while ((i -= 3) > 0);
  if(i != -1) { System.out.println("Not -1"); }
  String value = String.valueOf(i);
  if (!"-1".equalsIgnoreCase(value)) {
    System.out.println(value);
    System.out.println(i);
  }
}

अच्छा कोडिंग अभ्यास के सिर्फ दो अंक ...

  1. बल्कि उपयोग करें String.valueOf()
  2. कुछ कोडिंग मानक निर्दिष्ट करते हैं कि स्ट्रिंग शाब्दिकों को .equals()तर्क के बजाय लक्ष्य होना चाहिए , इस प्रकार NullPointerException को कम करना।

जिस तरह से मुझे यह नहीं मिला वह प्रयोग करने से था String.format()

public void test() {
  int i = 8;
  while ((i -= 3) > 0);
  String value = String.format("%d", i);
  if (!"-1".equalsIgnoreCase(value)) {
    System.out.println(value);
    System.out.println(i);
  }
}

... अनिवार्य रूप से ऐसा लगता है कि जावा को अपनी सांस पकड़ने के लिए थोड़े समय की आवश्यकता है :)

EDIT: यह पूरी तरह से संयोग हो सकता है, लेकिन प्रिंट आउट और ASCII तालिका के मान के बीच कुछ पत्राचार प्रतीत होता है ।

  • i= -1, प्रदर्शित चरित्र /(ASCII दशमलव 47 का मान है)
  • i= -2, प्रदर्शित चरित्र .(ASCII दशमलव 46 का मान है)
  • i= -3, प्रदर्शित चरित्र -(ASCII दशमलव 45 का मान है)
  • i= -4, प्रदर्शित चरित्र ,(ASCII दशमलव 44 का मान है)
  • i= -5, प्रदर्शित चरित्र +(ASCII दशमलव 43 का मान है)
  • i= -6, प्रदर्शित चरित्र *(ASCII दशमलव 42 का मान है)
  • i= -7, प्रदर्शित चरित्र )(ASCII दशमलव 41 का मान है)
  • i= -8, प्रदर्शित चरित्र ((ASCII दशमलव 40 का मान है)
  • i= -9, प्रदर्शित चरित्र '(ASCII दशमलव 39 का मान है)

वास्तव में क्या दिलचस्प है कि ASCII दशमलव 48 में वर्ण का मान है 0और 48 - 1 = 47 (वर्ण /), आदि ...


1
वह चरित्र का संख्यात्मक मान "/" "-1" है ??? यह कहां से आता है? ( (int)'/' == 47,; (char)-1अपरिभाषित 0xFFFFहै <यूनिकोड में एक चरित्र नहीं है)
user85421-Banned

1
char c = '/'; int a = Character.getNumericValue (c); Println (एक);
अंब्रो-आर

getNumericValue()दिए गए कोड से कैसे संबंधित है ??? और यह कैसे परिवर्तित -1होता है '/'??? क्यों नहीं '-', getNumericValue('-')है भी -1??? (बहुत सारे तरीकों पर वापस जाएँ -1)
user85421-Banned

@CarlosHeuberger, मैं चरित्र मान प्राप्त करने के लिए ( ) getNumericValue()पर चल रहा था । आप 100% सही हैं कि एएससीआईआई का दशमलव मान 47 होना चाहिए (यह वही था जिसकी मुझे उम्मीद भी थी), लेकिन उस बिंदु पर -1 लौट रहा था जैसा मैंने जोड़ा था । मैं उस भ्रम को देख सकता हूं जिसे आप संदर्भित कर रहे हैं और पोस्ट को अपडेट किया है। value//getNumericValue()System.out.println(Character.getNumericValue(value.toCharArray()[0]));
अंब्रो-आर

1

पता नहीं क्यों जावा इस तरह के यादृच्छिक उत्पादन दे रहा है, लेकिन यह मुद्दा आपके सहमति में iहै जो forलूप के अंदर के बड़े मूल्यों के लिए विफल रहता है ।

यदि आप अपने कोड के String value = i + "";साथ लाइन को प्रतिस्थापित String value = String.valueOf(i) ;करते हैं तो अपेक्षित रूप से कार्य करता है

अंतर +को स्ट्रिंग में बदलने के लिए कॉनटेनटेनेंस देशी है और छोटी गाड़ी हो सकती है (अजीब तरह से हम इसे अब शायद मिल रहे हैं) और इस तरह के मुद्दे का कारण बन सकते हैं।

नोट: मैंने लूप के लिए i के मूल्य को 10000 तक कम कर दिया है और मैंने +कॉन्सेप्टेशन के साथ समस्या का सामना नहीं किया है ।

इस मुद्दे को जावा हितधारकों को सूचित किया जाना चाहिए और वे उसी पर अपनी राय दे सकते हैं।

संपादित करें मैंने लूप के लिए i का मान 3 मिलियन में अपडेट किया और नीचे के रूप में त्रुटियों का एक नया सेट देखा:

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: -1
    at java.lang.Integer.getChars(Integer.java:463)
    at java.lang.Integer.toString(Integer.java:402)
    at java.lang.String.valueOf(String.java:3099)
    at solving.LoopOutPut.test(LoopOutPut.java:16)
    at solving.LoopOutPut.main(LoopOutPut.java:8)

मेरा जावा संस्करण 8 है।


1
मुझे नहीं लगता है कि स्ट्रिंग संयोजन मूल है - यह सिर्फ StringConcatFactory(OpenJDK 13) या StringBuilder(जावा 8) का उपयोग करता है
user85421-प्रतिबंधित

@CarlosHeuberger संभव भी। मुझे लगता है कि यह जावा 9 से है अगर इसे StringConcatFactory क्लास करना है। लेकिन जहाँ तक मुझे जावा 8 जावा डॉन तक पता है, टी सपोर्ट ऑपरेटर ओवरलोडिंग नहीं है
विनय प्रजापति

@Vinay, ने इसे भी आजमाया और हाँ यह काम करता है, लेकिन जिस क्षण आप लूप को 30000 से बढ़ाकर 3000000 से कहते हैं कि आपके पास एक ही मुद्दा है।
अंब्रो-आर

@ Ambro-r मैंने आपके सुझाए गए मूल्य के साथ प्रयास किया और मुझे Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: -1त्रुटि मिली । अजीब।
विनय प्रजापति

3
i + ""वास्तव new StringBuilder().append(i).append("").toString()में जावा 8 के रूप में संकलित किया गया है , और इसका उपयोग करने से अंततः उत्पादन भी होता है
user85421-प्रतिबंधित
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.