java - जावा int स्मृति उपयोग




arrays memory (5)

Int सरणी संदर्भ वास्तव में एक int primitive (8 बाइट बनाम 4) की तुलना में ढेर फ्रेम में और अधिक जगह लेता है। आप वास्तव में अधिक जगह का उपयोग कर रहे हैं।

लेकिन मुझे लगता है कि प्राथमिक कारण लोगों को पहला तरीका पसंद है क्योंकि यह स्पष्ट और अधिक सुगम है।

जब लोग अधिक स्याही शामिल होते हैं तो लोग वास्तव में दूसरी चीजों को बहुत करीब करते हैं।

जबकि मैं विभिन्न प्रकार के स्मृति उपयोग पर विचार कर रहा था, मैं थोड़ी उलझन में शुरू हुआ कि जावा विधि को पारित होने पर पूर्णांक के लिए स्मृति का उपयोग कैसे करता है।

कहो, मेरे पास निम्न कोड था:

public static void main (String[] args){
     int i = 4;
     addUp(i);
}

public static int addUp(int i){
     if(i == 0) return 0;
     else return addUp(i - 1);         
}

इस उदाहरण में, मैं सोच रहा हूं कि मेरा निम्नलिखित तर्क सही था या नहीं:

  • मैंने शुरुआत में पूर्णांक i = 4 के लिए एक स्मृति बनाई है। फिर मैं इसे एक विधि में पास करता हूं। हालांकि, चूंकि primitives जावा में इंगित नहीं हैं, addUp (i == 4) में, मैं एक और पूर्णांक i = 4 बना देता हूं। फिर बाद में, एक और addUp (i == 3), addUp (i == 2) है, addUp (i == 1), addUp (i == 0) जिसमें प्रत्येक बार, जब मान इंगित नहीं किया जाता है, तो स्मृति में एक नया i मान आवंटित किया जाता है।
  • फिर एक "int i" मान के लिए, मैंने 6 पूर्णांक मान यादों का उपयोग किया है।

हालांकि, अगर मैं हमेशा इसे एक सरणी के माध्यम से पास करना था:

public static void main (String[] args){
     int[] i = {4};
     // int tempI = i[0];
     addUp(i);
}

public static int addUp(int[] i){
     if(i[0] == 0) return 0;
     else return addUp(i[0] = i[0] - 1);         
}

- चूंकि मैं आकार 1 का एक पूर्णांक सरणी बना देता हूं और उसके बाद उस ऐडअप को पास करता हूं जिसे फिर से एडअप (i [0] == 3), एडअप (i [0] == 2), एडअप (i [0] के लिए पारित किया जाएगा == 1), ऐडअप (i [0] == 0), मुझे केवल 1 पूर्णांक सरणी मेमोरी स्पेस का उपयोग करना पड़ा है और इसलिए अब तक अधिक लागत प्रभावी है। इसके अलावा, अगर मैं पहले से ही int [i] के प्रारंभिक मान को संग्रहीत करने के लिए एक int मान बनाना चाहता था, तो मेरे पास अभी भी मेरा "मूल" मान है।

फिर यह मुझे सवाल पर ले जाता है, लोग जावा विधियों में int जैसे प्राइमेटिव क्यों पास करते हैं? क्या यह उन प्राइमेटिव्स के सरणी मानों को पास करने के लिए कहीं अधिक मेमोरी कुशल नहीं है? या किसी भी तरह का पहला उदाहरण अभी भी ओ (1) स्मृति है?

और इस सवाल के शीर्ष पर, मैं विशेष रूप से int [] और int का उपयोग करने के स्मृति अंतरों को आश्चर्यचकित करता हूं, विशेष रूप से 1 के आकार के लिए। अग्रिम धन्यवाद। मैं बस जावा के साथ अधिक मेमोरी कुशल होने का सोच रहा था और यह मेरे सिर पर आया।

सारे सवालों के जवाब देने के लिए धन्यवाद! मैं अभी जल्दी सोच रहा हूं कि क्या मैं प्रत्येक कोड की बड़ी-ओह स्मृति का "विश्लेषण" करना चाहता हूं, क्या उन्हें दोनों ओ (1) माना जाएगा या क्या यह मानना ​​गलत होगा?


असल में, जब आप किसी विधि को पैरामीटर के रूप में सरणी पास करते हैं - इस सरणी का संदर्भ हुड के नीचे पारित किया जाता है। सरणी ही ढेर पर संग्रहीत है। और संदर्भ आकार में 4 या 8 बाइट्स हो सकता है (सीपीयू आर्किटेक्चर, जेवीएम कार्यान्वयन आदि के आधार पर; और भी अधिक, जेएलएस कुछ भी नहीं कहता है कि संदर्भ में संदर्भ कितना बड़ा है)।

दूसरी तरफ, आदिम int मान हमेशा केवल 4 बाइट्स का उपभोग करता है और ढेर पर रहता है।


कई चीजे:

पहली बात बाल विभाजित होगी, लेकिन जब आप जावा में एक int पास करते हैं तो आप स्टैक पर 4 बाइट आवंटित कर रहे हैं, और जब आप एक सरणी पास करते हैं (क्योंकि यह एक संदर्भ है) तो आप वास्तव में 8 बाइट आवंटित कर रहे हैं (एक x64 आर्किटेक्चर मानते हैं) ढेर, साथ ही अतिरिक्त 4 बाइट जो ढेर में int को स्टोर करते हैं।

सबसे महत्वपूर्ण बात यह है कि सरणी में रहने वाले डेटा को ढेर में आवंटित किया जाता है, जबकि सरणी के संदर्भ को स्टैक पर आवंटित किया जाता है, जब एक पूर्णांक गुजरने पर कोई ढेर आवंटन आवश्यक नहीं होता है, तो आदिम केवल स्टैक में आवंटित होता है। समय के साथ ढेर आवंटन को कम करने का मतलब यह होगा कि कचरा कलेक्टर को साफ करने के लिए कम चीजें होंगी। जबकि स्टैक-फ्रेम की सफाई छोटी है और अतिरिक्त प्रसंस्करण की आवश्यकता नहीं है।

हालांकि, यह सब moot (imho) है क्योंकि व्यवहार में जब आपके पास चर और वस्तुओं के जटिल संग्रह होते हैं तो आप उन्हें कक्षा में एक साथ समूहित करने की संभावना रखते हैं। सामान्य रूप से, आपको JVM के प्रदर्शन की हर अंतिम बूंद को निचोड़ने की कोशिश करने के बजाय पठनीयता और रखरखाव को बढ़ावा देने के लिए लिखना चाहिए। जेवीएम बहुत तेज़ है, और हमेशा मूर का कानून बैकस्टॉप के रूप में होता है।

प्रत्येक के लिए बिग-ओ का विश्लेषण करना मुश्किल होगा क्योंकि एक वास्तविक तस्वीर प्राप्त करने के लिए आपको कचरा कलेक्टर के व्यवहार में कारक बनाना होगा और यह व्यवहार दोनों JVM स्वयं और किसी भी रनटाइम (जेआईटी) पर अत्यधिक निर्भर है। ऑप्टिमाइज़ेशन जो JVM ने आपके कोड में किया है।

कृपया डोनाल्ड Knuth के बुद्धिमान शब्दों को याद रखें कि "समयपूर्व अनुकूलन सभी बुराई की जड़ है"

कोड लिखें जो माइक्रो-ट्यूनिंग से बचाता है, कोड जो पठनीयता और रखरखाव को बढ़ावा देता है, लंबे समय तक बेहतर किराया देगा।


चाहे ये विधियां ओ (1) या ओ (एन) संकलक पर निर्भर करती हैं। (यहां एन i या i[0] का मान है।) यदि कंपाइलर पूंछ-रिकर्सन ऑप्टिमाइज़ेशन का उपयोग करता है तो पैरामीटर, स्थानीय चर, और वापसी पते के लिए स्टैक स्पेस का पुन: उपयोग किया जा सकता है और कार्यान्वयन तब ओ ( 1) अंतरिक्ष के लिए। अनुपस्थित पूंछ-पुनरावर्तन अनुकूलन अंतरिक्ष जटिलता समय जटिलता, ओ (एन) के समान ही है।

मूल रूप से पूंछ-रिकर्सन ऑप्टिमाइज़ेशन रकम (इस मामले में) आपके कोड को फिर से लिखने वाले कंपाइलर को

public static int addUp(int i){
     while(i != 0) i = i-1 ;
     return 0;        
}

या

public static int addUp(int[] i){
     while(i[0] != 0) i[0] = i[0] - 1 ;
     return 0 ;
}

एक अच्छा अनुकूलक लूप को और अधिक अनुकूलित कर सकता है।

जहां तक ​​मुझे पता है, कोई जावा कंपाइलर्स वर्तमान में पूंछ-रिकर्सन ऑप्टिमाइज़ेशन को लागू नहीं करता है, लेकिन कोई तकनीकी कारण नहीं है कि यह कई मामलों में नहीं किया जा सकता है।


यदि आपकी धारणा यह है कि कार्यों में पारित तर्क आवश्यक रूप से मेमोरी (जो कि रास्ते से गलत है) का उपभोग करते हैं, तो आपके दूसरे उदाहरण में जो सरणी पास करता है, सरणी के संदर्भ की एक प्रति बनाई जाती है। यह संदर्भ वास्तव में एक int से बड़ा हो सकता है, यह छोटा होने की संभावना नहीं है।





int