LINUX.ORG.RU

[ООП] Время жизни объекта

 


0

0

Доброго времени суток

В связи с недавним обсуждением здесь же возник вопрос. В том ООП который в C++ очень важной особенностью объекта является продолжительность его жизни - многие техники программирования так или иначе отталкиваются от этого понятия (RAII, локальные блоки, auto_ptr<>, etc). А как обстоит с этим дело в языках с GC (поддерживающих ООП в той или иной мере - прежде всего Java, Python, Ruby, Smalltalk и Eiffel; ну и насчёт CL тоже интересно) ? Насколько детерменировано там понятие продолжительности жизни обьекта и насколько оно используется в разработке ? Особенно хотелось бы услышать мнение людей, писавших и на C++ и на каком-то из перечисленных ЯП. Отсылать к литературе не надо, интересует именно личный опыт

Заранее спасибо

★★★★★

В Питоне и Яве на время жизни объекта практически не обращаешь внимания (хотя на Питоне log-running программ я не писал :)).

tailgunner ★★★★★
()

Если объект использует какие либо иные ресурсы кроме памяти (например, файловые дескрипторы), то его время жизни надо контроллировать.

anonymous
()

Я отталкиваюсь от того, что объект будет удалён не ранее последнего к нему обращения.

Конструкций типа блоков с mutexlocker-ом, который лочит при входе и освобождает его по завершение блока(читай -- по завершению своей области видимости) в яве я не видел. Там для этих целей более логичными выглядят аннотации, например, как сделано с транзакциями в ejb.

Ещё в той же яве есть какое-то подобие деструктора (ключевое слово fonalize вроде бы), но я с ним ни разу не встречался и необходимоси его использовать не возникало.

gaa ★★
()
Ответ на: комментарий от anonymous

> Если объект использует какие либо иные ресурсы кроме памяти (например, файловые дескрипторы), то его время жизни надо контроллировать.

Не совсем так. Просто подход немного другой: если в C++ можно было release ресурса воткнуть в деструктор и успокоиться, то в языках с GC, обычно, это происходит явно, через методы типа close, release, free и пр. А на случай исключений используется finally.

swizard
()

В Яве на поверхности все просто: все объекты создаются "динамически" - нет явного разделения на объекты в стеке и в куче, и за временем их жизни следит GC. Если копать глубже - есть класс Reference и разные типы ссылок - Soft, Weak, Phantom.

shumer
()

Буду говорить про джаву.

Физическая жизнь объекта в джаве не детерминирована. Т.е. момент сбора объекта GC не определён. Объект может не быть собран вообще никогда, даже после нормального завершения программы.

В принципе у объекта есть виртуальный метод finalize, в нём можно, к примеру, закрыть файловый дескриптор, если уж программист был настолько рассеян, что забыл это сделать сам. Ещё можно в логи пожаловаться на сей факт. Т.е. хоть как-нибудь но попробуем освободить ресурсы. Но это ненадёжно, и должно использоваться в качестве последнего шанса.

Логическая же жизнь объекта должна быть строго детерминирована. Пренебрежение этим, зачастую, ведёт к утечкам памяти, которые хоть и не совсем настоящие, но память утекает самая что ни на есть настоящая.

Иногда объект владеет своими частями. Тогда никаких усилий не нужно предпринимать, они умрут вместе с ним. Иногда нужно специально занулять ссылки на объект, указывая на то, что время жизни этого объекта закончено.

Некоторые объекты хранят ресурсы, вроде файловых дескрипторов, сокетов, логов и т.д. Такие ресурсы надо освобождать руками, хотя это и не является аналогом понятия жизни объекта.

Если единственный язык до этого был С++, с его value-семантикой и активно используемыми деструкторами, могут возникнуть сложности с пониманием, т.к. здесь техника совсем другая, в чём то кажущаяся неправильной (в С++ деструктор всё делал за нас, а здесь руками пиши try-finally, закрывай всё руками, вот ведь безобразие какое, ява отстой!), но на практике особых проблем не возникает.

Legioner ★★★★★
()
Ответ на: комментарий от Legioner

И, да, меня назовут быдлокодером, но на С++ без valgrind-а мне следить за памятью тяжело. Всякие boost::shared_ptr, std::vector-ы конечно очень помогают в сравнении с чистым С, но нет-нет да утечёт где-нибудь ссылка на инвалидировавшуюся память. Да и утяжеляют программу все эти вспомогательные zero-overhead классы. Там std::string пришлось вернуть по значению, там std::shared_ptr счётчиком в цикле щёлкает. А начнёшь оптимизировать, голыми указателями размахивать, и течь всё начинает. Когда про память не нужно думать вообще, это сильно облегчает разработку /мне/

Legioner ★★★★★
()
Ответ на: комментарий от Legioner

> на практике особых проблем не возникает.

Вспомнились монструозные блоки закрывания dataSource`ов.

wfrr ★★☆
()
Ответ на: комментарий от shumer

>Если копать глубже - есть класс Reference и разные типы ссылок - Soft, Weak, Phantom.

А зачем они все? Эти Referencы

anonymous
()
Ответ на: комментарий от wfrr

public interface ResultSetVisitor {
    void visit(ResultSet rs) throws SQLException, InvalidDatabaseStateException;
}


public class SQLUtils {
    private static final Logger logger = Logger.getLogger(SQLUtils.class);
    
    public static final SQLNull NULL_INTEGER = new SQLNull(Types.INTEGER);
    
    private final Connection connection;
    private final Map<String, PreparedStatement> statementsCache = new HashMap<String, PreparedStatement>();
    
    public SQLUtils(Connection connection) {
        this.connection = connection;
    }
    
    private static String arrayToString(Object[] params) {
        StringBuilder sb = new StringBuilder();
        boolean first = true;
        for (Object o : params) {
            if (first) {
                first = false;
            } else {
                sb.append(", ");
            }
            if (o instanceof String) {
                sb.append('\'').append((String) o).append('\'');
            }
            else {
                sb.append(o);
            }
        }
        return sb.toString();
    }
    
    public void query(String sql, ResultSetVisitor visitor, Object ... params) throws InvalidDatabaseStateException {
        if (logger.isDebugEnabled()) {
            logger.debug("Executing SQL query: \"" + sql + "\", params: " + arrayToString(params));
        }
        
        try {
            PreparedStatement statement = prepareStatement(sql);
            setPreparedStatementParameters(statement, params);
            ResultSet rs = statement.executeQuery();
            try {
                while (rs.next()) {
                    visitor.visit(rs);
                }
            } finally {
                rs.close();
            }
        } catch (SQLException e) {
            throw new UnrecoverableDatabaseError(e);
        }
    }
    
    public Connection getConnection() {
        return connection;
    }
    
    public PreparedStatement prepareStatement(String sql) {
        logger.debug("Preparing SQL query: \"" + sql + "\"");
        try {
            PreparedStatement statement = statementsCache.get(sql);
            if (statement == null) {
                statement = connection.prepareStatement(sql);
                statementsCache.put(sql, statement);
            }
            return statement;
        } catch (SQLException e) {
            throw new UnrecoverableDatabaseError(e);
        }
    }

    private void setPreparedStatementParameters(PreparedStatement statement, Object ... params) throws SQLException {
        statement.clearParameters();
        int index = 1;
        for (Object obj : params) {
            if (obj == null) {
                throw new IllegalArgumentException("params must not be null. Use SQLUtils.NULL_type instead.");
            }
            if (obj instanceof SQLNull) {
                statement.setNull(index, ((SQLNull) obj).getSqlType());
            } else {
                statement.setObject(index, obj);
            }
            ++index;
        }
    }    
    
    private static class SQLNull {
        private final int sqlType;
        
        SQLNull(int sqlType) {
            this.sqlType = sqlType;
        }
        
        int getSqlType() {
            return sqlType;
        }
        
        @Override
        public String toString() {
            return "NULL";
        }
    }
    
    public void clearCache() {
        for (PreparedStatement statement : statementsCache.values()) {
            try {
                statement.close();
            } catch (SQLException e) {
                throw new UnrecoverableDatabaseError(e);
            }
        }
        statementsCache.clear();
    }
}

Использование:
       String sql = 
            " select sid" +
            "      , blablabla" +
            " from table" +
            " where blabla = ?;
        
        sqlUtils.query(sql, new ResultSetVisitor() {
            public void visit(ResultSet rs) throws SQLException, InvalidDatabaseStateException {
                // do anything
            }
        }, notification.getSid());

Руками писать никаких try-catch не надо, и закрывать почти ничего не надо. Если Statement-ы не кешировать, то и их можно сразу закрывать.

В spring-е кстати есть практически то же самое.

Legioner ★★★★★
()
Ответ на: комментарий от anonymous

>>Если копать глубже - есть класс Reference и разные типы ссылок - Soft, Weak, Phantom.

>А зачем они все? Эти Referencы

Они описывают разную степень "достижимости" объектов, и позволяют в некоторой степени управлять временем его жизни. Например, если объект достижим только по weak ссылке, GC его удаляет. Если по soft ссылке, то там посложнее - GC может удалить, а может и нет, зависит от наличия свободной памяти, по идее должно учитываться и время создания ссылки, старые удаляются раньше.

shumer
()
Ответ на: комментарий от shumer

>Они описывают разную степень "достижимости" объектов, и позволяют в некоторой степени управлять временем его жизни. Например, если объект достижим только по weak ссылке, GC его удаляет. Если по soft ссылке, то там посложнее - GC может удалить, а может и нет, зависит от наличия свободной памяти, по идее должно учитываться и время создания ссылки, старые удаляются раньше.

Это понятно. А для чего они? RecordSetы из БД кэшировать в JTableView? Или еще что-то?

anonymous
()
Ответ на: комментарий от anonymous

Вообще, для хранения объектов, потерять которые, в случае острой нехватки памяти, не жалко. Кэширование - как вариант.

shumer
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.