संभव ढेर प्रदूषण varargs पैरामीटर के माध्यम से


433

मैं समझता हूं कि यह जावा 7 के साथ होता है जब एक सामान्य प्रकार के साथ varargs का उपयोग किया जाता है;

लेकिन मेरा सवाल है ..

वास्तव में ग्रहण का क्या मतलब है जब यह कहता है कि "इसका उपयोग संभावित रूप से ढेर को प्रदूषित कर सकता है?"

तथा

नया @SafeVarargsएनोटेशन इसे कैसे रोकता है?


8
यहाँ विवरण: docs.oracle.com/javase/specs/jls/se7/html/…
assylias


मैं इसे अपने संपादक में देख रहा हूँ:Possible heap pollution from parameterized vararg type
अलेक्जेंडर मिल्स

जवाबों:


252

हीप प्रदूषण एक तकनीकी शब्द है। यह उन संदर्भों को संदर्भित करता है जिनके पास एक प्रकार है जो उस वस्तु का सुपरटाइप नहीं है जो वे इंगित करते हैं।

List<A> listOfAs = new ArrayList<>();
List<B> listOfBs = (List<B>)(Object)listOfAs; // points to a list of As

इससे "अनएक्सप्लेनएबल" हो सकता है ClassCastException

// if the heap never gets polluted, this should never throw a CCE
B b = listOfBs.get(0); 

@SafeVarargsयह बिल्कुल नहीं रोकता है। हालाँकि, ऐसी विधियाँ हैं जो सिद्ध रूप से ढेर को प्रदूषित नहीं करेंगी, कंपाइलर सिर्फ इसे साबित नहीं कर सकते हैं। पहले, ऐसे API के कॉल करने वालों को कष्टप्रद चेतावनियाँ मिलती थीं जो पूरी तरह से निरर्थक थीं लेकिन हर कॉल साइट पर इसे दबा दिया जाता था। अब एपीआई लेखक एक बार घोषणा स्थल पर इसे दबा सकता है।

हालाँकि, यदि विधि वास्तव में सुरक्षित नहीं है , तो उपयोगकर्ताओं को अब चेतावनी नहीं दी जाएगी।


2
तो क्या हम कह रहे हैं कि ढेर प्रदूषित है क्योंकि इसमें ऐसे संदर्भ हैं जिनके प्रकार वे नहीं हैं जिनकी हम उम्मीद कर सकते हैं? (सूची <A> बनाम सूची <बी> आपके उदाहरण में)
हर्ट्ज़स्प्रंग


30
यह उत्तर इस बात की एक सूक्ष्म व्याख्या है कि ढेर प्रदूषण क्या है, लेकिन यह वास्तव में यह नहीं बताता है कि विशेष रूप से वार्गर्स के कारण ऐसा क्यों होता है क्योंकि यह एक विशिष्ट चेतावनी को वारंट करता है।
Dolda2000

4
मुझे भी, मुझे जानकारी याद आ रही है कि यह सुनिश्चित करने के लिए कि मेरे कोड में यह समस्या नहीं है (उदाहरण के लिए, मुझे कैसे पता चलेगा कि यह @SafeVarargs जोड़ने के लिए पर्याप्त कठोर है)
डैनियल एल्डर

237

जब आप घोषणा करते हैं

public static <T> void foo(List<T>... bar) संकलक इसे करने के लिए धर्मान्तरित

public static <T> void foo(List<T>[] bar) फिर तो

public static void foo(List[] bar)

फिर यह खतरा उत्पन्न होता है कि आप गलती से सूची में गलत मान असाइन कर देंगे और कंपाइलर किसी भी त्रुटि को ट्रिगर नहीं करेगा। उदाहरण के लिए, यदि Tएक Stringनिम्न कोड त्रुटि के बिना संकलित होगा, लेकिन रनटाइम में विफल हो जाएगा:

// First, strip away the array type (arrays allow this kind of upcasting)
Object[] objectArray = bar;

// Next, insert an element with an incorrect type into the array
objectArray[0] = Arrays.asList(new Integer(42));

// Finally, try accessing the original array. A runtime error will occur
// (ClassCastException due to a casting from Integer to String)
T firstElement = bar[0].get(0);

यदि आपने यह सुनिश्चित करने के लिए विधि की समीक्षा की है कि इसमें ऐसी कमजोरियां नहीं हैं, तो आप @SafeVarargsचेतावनी को दबाने के लिए इसे एनोटेट कर सकते हैं । इंटरफेस के लिए, का उपयोग करें @SuppressWarnings("unchecked")

यदि आपको यह त्रुटि संदेश मिलता है:

वर्गास विधि गैर-पुन: परिवर्तनीय वैरैगस पैरामीटर से ढेर प्रदूषण का कारण बन सकती है

और आप सुनिश्चित हैं कि आपका उपयोग सुरक्षित है तो आपको @SuppressWarnings("varargs")इसके बजाय उपयोग करना चाहिए । देखें इस विधि के लिए क्या @SafeVarargs एक उपयुक्त एनोटेशन है? और https://stackoverflow.com/a/14252221/14731 त्रुटि के इस दूसरे प्रकार का एक अच्छा विवरण के लिए।

संदर्भ:


2
मुझे लगता है कि मैं बेहतर समझ रहा हूं। खतरे तब आते हैं जब आप वैरग को कास्ट करते हैं Object[]। जब तक आप नहीं डालते हैं Object[], तब तक यह लगता है कि आपको ठीक होना चाहिए।
djikyb

3
एक बेवकूफ चीज के उदाहरण के रूप में आप कर सकते हैं static <T> void bar(T...args) { ((Object[])args)[0] = "a"; }:। और फिर कॉल करें bar(Arrays.asList(1,2));
djikybb

1
@djeikyb अगर खतरे केवल तभी उत्पन्न होते हैं जब मैं Object[]संकलक को चेतावनी देता हूं कि अगर मैं नहीं करता हूं तो क्यों होगा? संकलन-समय पर इसे जांचना काफी आसान होना चाहिए, आखिरकार (अगर मैं इसे इसी तरह के हस्ताक्षर के साथ किसी अन्य फ़ंक्शन को पास नहीं करता हूं, तो उस स्थिति में दूसरे फ़ंक्शन को चेतावनी को ट्रिगर करना चाहिए)। मुझे विश्वास नहीं है कि यह वास्तव में चेतावनी का मूल है ("आप सुरक्षित हैं यदि आप डाली नहीं हैं"), और मुझे अभी भी समझ में नहीं आता कि मैं किस मामले में ठीक हूं।
Qw3ry

5
@djeikyb आप बिलकुल वैसी ही बेवकूफी कर सकते हैं जैसे बिना पैरामाईज्ड वैरगेज (जैसे bar(Integer...args)) के। तो फिर इस चेतावनी का क्या मतलब है?
Vasiliy Vlasov

3
@VasiliyVlasov यह समस्या केवल मानकीकृत varargs के लिए प्रासंगिक है। यदि आप गैर-टाइप किए गए सरणियों के साथ एक ही काम करने की कोशिश करते हैं, तो रनटाइम आपको गलत प्रकार को सरणी में डालने से रोक देगा। संकलक आप चेतावनी है कि क्रम गलत व्यवहार को रोकने में असमर्थ हो जाएगा क्योंकि पैरामीटर प्रकार रनटाइम पर अज्ञात है (इसके विपरीत, सरणियों करते कार्यावधि में उनके गैर सामान्य तत्वों के प्रकार जानते हैं)।
गिली

8

@SafeVarargs यह होने से नहीं रोकता है, हालांकि यह अनिवार्य है कि संकलक कोड का उपयोग करते समय संकलक है।

http://docs.oracle.com/javase/7/docs/api/java/lang/SafeVarargs.html इस बारे में विस्तार से बताते हैं।

हीप प्रदूषण तब होता है जब आपको ClassCastExceptionजेनेरिक इंटरफ़ेस पर एक ऑपरेशन करते समय मिलता है और इसमें घोषित की तुलना में एक अन्य प्रकार होता है।


इसके उपयोग पर अतिरिक्त संकलक प्रतिबंध विशेष रूप से प्रासंगिक नहीं लगते हैं।
पॉल बेलोरा

6

जब आप varargs का उपयोग करते हैं, तो इसका परिणाम Object[]तर्कों को पकड़ना हो सकता है।

भागने के विश्लेषण के कारण, जेआईटी इस सरणी निर्माण को दूर कर सकता है। (कुछ समय में से एक जो मैंने पाया है वह ऐसा करता है) इसकी दूर से अनुकूलित होने की गारंटी नहीं है, लेकिन मैं इसके बारे में चिंता नहीं करूंगा जब तक कि आप अपनी मेमोरी प्रोफाइलर में इसका मुद्दा नहीं देखते।

AFAIK @SafeVarargsकंपाइलर द्वारा एक चेतावनी को दबा देता है और यह नहीं बदलता है कि JIT कैसे व्यवहार करता है।


6
दिलचस्प है, हालांकि यह वास्तव में उसके सवाल का जवाब नहीं है @SafeVarargs
पॉल बेलोरा

1
नहीं। यही कारण है कि हीप प्रदूषण नहीं है। "ढेर प्रदूषण तब होता है जब एक पैरामीटर प्रकार का एक चर एक ऑब्जेक्ट को संदर्भित करता है जो उस पैरामीटर प्रकार का नहीं होता है।" रेफरी: docs.oracle.com/javase/tutorial/java/generics/...
डोराडस

1

इसका कारण यह है कि varargs एक गैर-पैरामीरिड ऑब्जेक्ट सरणी के साथ बुलाया जाने का विकल्प देता है। इसलिए यदि आपका प्रकार List <A> ... था, तो इसे List [] गैर-वैरग प्रकार के साथ भी कहा जा सकता है।

यहाँ एक उदाहरण है:

public static void testCode(){
    List[] b = new List[1];
    test(b);
}

@SafeVarargs
public static void test(List<A>... a){
}

जैसा कि आप देख सकते हैं सूची [] बी में किसी भी प्रकार के उपभोक्ता शामिल हो सकते हैं, और फिर भी यह कोड संकलित करता है। यदि आप varargs का उपयोग करते हैं, तो आप ठीक हैं, लेकिन यदि आप टाइप-इरेज़र - शून्य परीक्षण (सूची []) के बाद विधि परिभाषा का उपयोग करते हैं - तो कंपाइलर टेम्पलेट पैरामीटर प्रकारों की जाँच नहीं करेगा। @SafeVarargs इस चेतावनी को दबा देगा।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.